From 641b96e4e1e5fc773aa5a813c4c649351340e3fb Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Wed, 29 Nov 2023 22:36:20 +0100 Subject: [PATCH 01/10] adding ScalaObject.scala --- .../fasterxml/jackson/module/scala/util/ScalaObject.scala | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala new file mode 100644 index 00000000..23e6800b --- /dev/null +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala @@ -0,0 +1,7 @@ +package com.fasterxml.jackson.module.scala.util + +object ScalaObject { + + private val MODULE_FIELD_NAME = "MODULE$" + +} From 49e62348940e1b3a26df3f0b1920d422f78f446b Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Wed, 29 Nov 2023 23:07:32 +0100 Subject: [PATCH 02/10] correcting ScalaObjectDeserializerResolver --- .../deser/ScalaObjectDeserializerModule.scala | 27 +++++++++---------- .../module/scala/util/ScalaObject.scala | 22 +++++++++++++++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala index fe061873..43333e43 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala @@ -1,37 +1,34 @@ package com.fasterxml.jackson.module.scala.deser import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind._ import com.fasterxml.jackson.databind.deser.Deserializers import com.fasterxml.jackson.databind.deser.std.StdDeserializer -import com.fasterxml.jackson.databind._ import com.fasterxml.jackson.module.scala.JacksonModule -import com.fasterxml.jackson.module.scala.util.ClassW +import com.fasterxml.jackson.module.scala.util.ScalaObject import scala.languageFeature.postfixOps -import scala.util.control.NonFatal -private class ScalaObjectDeserializer(clazz: Class[_]) extends StdDeserializer[Any](classOf[Any]) { - override def deserialize(p: JsonParser, ctxt: DeserializationContext): Any = { - try { - clazz.getField("MODULE$").get(null) - } catch { - case NonFatal(_) => null - } - } +private class ScalaObjectDeserializer(scalaObject: Any) extends StdDeserializer[Any](classOf[Any]) { + override def deserialize(p: JsonParser, ctxt: DeserializationContext): Any = scalaObject } private object ScalaObjectDeserializerResolver extends Deserializers.Base { override def findBeanDeserializer(javaType: JavaType, config: DeserializationConfig, beanDesc: BeanDescription): JsonDeserializer[_] = { val clazz = javaType.getRawClass - if (ClassW(clazz).isScalaObject) - new ScalaObjectDeserializer(clazz) - else null + clazz match { + case ScalaObject(value) => new ScalaObjectDeserializer(value) + case _ => null + } } } trait ScalaObjectDeserializerModule extends JacksonModule { override def getModuleName: String = "ScalaObjectDeserializerModule" - this += { _ addDeserializers ScalaObjectDeserializerResolver } + + this += { + _ addDeserializers ScalaObjectDeserializerResolver + } } object ScalaObjectDeserializerModule extends ScalaObjectDeserializerModule diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala index 23e6800b..045d6b0d 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala @@ -1,7 +1,29 @@ package com.fasterxml.jackson.module.scala.util +import java.lang.reflect.Field + object ScalaObject { private val MODULE_FIELD_NAME = "MODULE$" + private def getStaticField(field: Field): Option[Any] = + try Some(field.get(null)) + catch { + case _: NullPointerException => None + } + + private def moduleFieldOption(clazz: Class[_]): Option[Field] = + try Some(clazz.getDeclaredField(MODULE_FIELD_NAME)) + catch { + case _: NoSuchFieldException => None + } + + private def moduleFieldValue(clazz: Class[_]): Option[Any] = for { + moduleField <- moduleFieldOption(clazz) + value <- getStaticField(moduleField) + } yield value + + def unapply(clazz: Class[_]): Option[Any] = + if (clazz.getSimpleName.endsWith("$")) moduleFieldValue(clazz) + else None } From 9cf38e75a4cef5e6e89b3c58e44f6473d9487407 Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Wed, 29 Nov 2023 23:27:11 +0100 Subject: [PATCH 03/10] correcting adding deserializers --- .../module/scala/deser/ScalaObjectDeserializerModule.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala index 43333e43..a259f94b 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala @@ -26,9 +26,7 @@ private object ScalaObjectDeserializerResolver extends Deserializers.Base { trait ScalaObjectDeserializerModule extends JacksonModule { override def getModuleName: String = "ScalaObjectDeserializerModule" - this += { - _ addDeserializers ScalaObjectDeserializerResolver - } + this += { _ addDeserializers ScalaObjectDeserializerResolver } } object ScalaObjectDeserializerModule extends ScalaObjectDeserializerModule From 65cf493647eb2eef68f389f35021770554378b29 Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Wed, 29 Nov 2023 23:28:15 +0100 Subject: [PATCH 04/10] removing line --- .../module/scala/deser/ScalaObjectDeserializerModule.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala index a259f94b..2d50e199 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala @@ -25,7 +25,6 @@ private object ScalaObjectDeserializerResolver extends Deserializers.Base { trait ScalaObjectDeserializerModule extends JacksonModule { override def getModuleName: String = "ScalaObjectDeserializerModule" - this += { _ addDeserializers ScalaObjectDeserializerResolver } } From dc128d04fc8d7bcd347e90e16826493c513af3ec Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Thu, 30 Nov 2023 17:49:18 +0100 Subject: [PATCH 05/10] adding fallback if clazz is null --- .../deser/ScalaObjectDeserializerModule.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala index 2d50e199..e4a3d681 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala @@ -16,16 +16,21 @@ private class ScalaObjectDeserializer(scalaObject: Any) extends StdDeserializer[ private object ScalaObjectDeserializerResolver extends Deserializers.Base { override def findBeanDeserializer(javaType: JavaType, config: DeserializationConfig, beanDesc: BeanDescription): JsonDeserializer[_] = { val clazz = javaType.getRawClass - clazz match { - case ScalaObject(value) => new ScalaObjectDeserializer(value) - case _ => null - } + + Option(clazz) + .collect { + case ScalaObject(value) => new ScalaObjectDeserializer(value) + } + .orNull } } trait ScalaObjectDeserializerModule extends JacksonModule { override def getModuleName: String = "ScalaObjectDeserializerModule" - this += { _ addDeserializers ScalaObjectDeserializerResolver } + + this += { + _ addDeserializers ScalaObjectDeserializerResolver + } } object ScalaObjectDeserializerModule extends ScalaObjectDeserializerModule From f40112ba520d81e3ed1a253e4cd131011b4126cc Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Thu, 30 Nov 2023 17:51:08 +0100 Subject: [PATCH 06/10] correcting formatting --- .../module/scala/deser/ScalaObjectDeserializerModule.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala index e4a3d681..309a19e3 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaObjectDeserializerModule.scala @@ -27,10 +27,7 @@ private object ScalaObjectDeserializerResolver extends Deserializers.Base { trait ScalaObjectDeserializerModule extends JacksonModule { override def getModuleName: String = "ScalaObjectDeserializerModule" - - this += { - _ addDeserializers ScalaObjectDeserializerResolver - } + this += { _ addDeserializers ScalaObjectDeserializerResolver } } object ScalaObjectDeserializerModule extends ScalaObjectDeserializerModule From 8981eb1c2060bb5383ef47da59905467b0d94972 Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Thu, 30 Nov 2023 18:17:44 +0100 Subject: [PATCH 07/10] adding ScalaObjectTest --- .../module/scala/util/ScalaObject.scala | 2 +- .../module/scala/util/ScalaObjectTest.scala | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala index 045d6b0d..1df6ed42 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala @@ -9,7 +9,7 @@ object ScalaObject { private def getStaticField(field: Field): Option[Any] = try Some(field.get(null)) catch { - case _: NullPointerException => None + case _: NullPointerException | _: IllegalAccessException => None } private def moduleFieldOption(clazz: Class[_]): Option[Field] = diff --git a/src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala b/src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala new file mode 100644 index 00000000..95373c2c --- /dev/null +++ b/src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala @@ -0,0 +1,53 @@ +package com.fasterxml.jackson.module.scala.util + +import org.scalatest.matchers.should.Matchers.{contain, convertToAnyShouldWrapper, empty} +import org.scalatest.wordspec.AnyWordSpecLike + +object TestObject + +case object TestCaseObject + +class TestClass + +class TestClassWithModuleField { + val MODULE$: TestClassWithModuleField = this +} + +class TestClass$ + +class TestClassWithModuleField$ { + val MODULE$: TestClassWithModuleField$ = this +} + +class ScalaObjectTest extends AnyWordSpecLike { + + "ScalaObject" must { + "return Some(TestObject) for unapply(TestObject.getClass)" in { + ScalaObject.unapply(TestObject.getClass) should contain(TestObject) + } + + "return Some(TestCaseObject) for unapply(TestCaseObject.getClass)" in { + ScalaObject.unapply(TestCaseObject.getClass) should contain(TestCaseObject) + } + + "return None for unapply(testClassInstance.getClass)" in { + val testClassInstance = new TestClass + ScalaObject.unapply(testClassInstance.getClass) shouldBe empty + } + + "return None for unapply(testClassWithModuleFieldInstance.getClass)" in { + val testClassWithModuleFieldInstance = new TestClassWithModuleField + ScalaObject.unapply(testClassWithModuleFieldInstance.getClass) shouldBe empty + } + + "return None for unapply(testClassWithADollarInstance.getClass)" in { + val testClassWithADollarInstance = new TestClass$ + ScalaObject.unapply(testClassWithADollarInstance.getClass) shouldBe empty + } + + "return None for unapply(testClassWithModuleFieldAndADollarInstance.getClass)" in { + val testClassWithModuleFieldAndADollarInstance = new TestClassWithModuleField$ + ScalaObject.unapply(testClassWithModuleFieldAndADollarInstance.getClass) shouldBe empty + } + } +} From fb860bead9812bfc5985216558630307850471ec Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Thu, 30 Nov 2023 20:25:15 +0100 Subject: [PATCH 08/10] changing to private [scala] and to getName instead of getSimpleName --- .../com/fasterxml/jackson/module/scala/util/ScalaObject.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala index 1df6ed42..60414126 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/ScalaObject.scala @@ -2,7 +2,7 @@ package com.fasterxml.jackson.module.scala.util import java.lang.reflect.Field -object ScalaObject { +private [scala] object ScalaObject { private val MODULE_FIELD_NAME = "MODULE$" @@ -24,6 +24,6 @@ object ScalaObject { } yield value def unapply(clazz: Class[_]): Option[Any] = - if (clazz.getSimpleName.endsWith("$")) moduleFieldValue(clazz) + if (clazz.getName.endsWith("$")) moduleFieldValue(clazz) else None } From 517a8a444a07591cd6e73fc35ea9318ffbc0ba9f Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Thu, 30 Nov 2023 20:33:41 +0100 Subject: [PATCH 09/10] adding test for wrapper --- .../jackson/module/scala/util/ScalaObjectTest.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) rename src/test/{scala-2.+ => scala}/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala (87%) diff --git a/src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala b/src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala similarity index 87% rename from src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala rename to src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala index 95373c2c..738d43de 100644 --- a/src/test/scala-2.+/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala +++ b/src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala @@ -19,6 +19,12 @@ class TestClassWithModuleField$ { val MODULE$: TestClassWithModuleField$ = this } +object BarWrapper { + object Bar { + final case class Baz() + } +} + class ScalaObjectTest extends AnyWordSpecLike { "ScalaObject" must { @@ -49,5 +55,10 @@ class ScalaObjectTest extends AnyWordSpecLike { val testClassWithModuleFieldAndADollarInstance = new TestClassWithModuleField$ ScalaObject.unapply(testClassWithModuleFieldAndADollarInstance.getClass) shouldBe empty } + + "return None for unapply(bazInstance.getClass)" in { + val bazInstance = BarWrapper.Bar.Baz() + ScalaObject.unapply(bazInstance.getClass) shouldBe empty + } } } From 17f636261a59f810270ed86b48d9fedb31841f10 Mon Sep 17 00:00:00 2001 From: Marcin Belicki Date: Thu, 30 Nov 2023 20:44:00 +0100 Subject: [PATCH 10/10] deleting classes ending with $ --- .../module/scala/util/ScalaObjectTest.scala | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala b/src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala index 738d43de..d3b96f2e 100644 --- a/src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala +++ b/src/test/scala/com/fasterxml/jackson/module/scala/util/ScalaObjectTest.scala @@ -13,12 +13,6 @@ class TestClassWithModuleField { val MODULE$: TestClassWithModuleField = this } -class TestClass$ - -class TestClassWithModuleField$ { - val MODULE$: TestClassWithModuleField$ = this -} - object BarWrapper { object Bar { final case class Baz() @@ -46,16 +40,6 @@ class ScalaObjectTest extends AnyWordSpecLike { ScalaObject.unapply(testClassWithModuleFieldInstance.getClass) shouldBe empty } - "return None for unapply(testClassWithADollarInstance.getClass)" in { - val testClassWithADollarInstance = new TestClass$ - ScalaObject.unapply(testClassWithADollarInstance.getClass) shouldBe empty - } - - "return None for unapply(testClassWithModuleFieldAndADollarInstance.getClass)" in { - val testClassWithModuleFieldAndADollarInstance = new TestClassWithModuleField$ - ScalaObject.unapply(testClassWithModuleFieldAndADollarInstance.getClass) shouldBe empty - } - "return None for unapply(bazInstance.getClass)" in { val bazInstance = BarWrapper.Bar.Baz() ScalaObject.unapply(bazInstance.getClass) shouldBe empty