From 1a06224d6dd47b0779822d69c8404baa8e6b58e6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 30 Nov 2023 21:02:21 +0100 Subject: [PATCH 1/6] refactor scala object deserialization --- .../deser/ScalaObjectDeserializerModule.scala | 17 +++++------------ .../jackson/module/scala/util/Classes.scala | 7 +++++-- 2 files changed, 10 insertions(+), 14 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 fe061873a..b6908df6d 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 @@ -10,22 +10,15 @@ import com.fasterxml.jackson.module.scala.util.ClassW 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(value: Any) extends StdDeserializer[Any](classOf[Any]) { + override def deserialize(p: JsonParser, ctxt: DeserializationContext): Any = value } 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 + ClassW(javaType.getRawClass).getModuleField.flatMap { field => + Option(field.get(null)) + }.map(new ScalaObjectDeserializer(_)).orNull } } diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala index c0aaf2f70..5fff77042 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala @@ -1,5 +1,6 @@ package com.fasterxml.jackson.module.scala.util +import java.lang.reflect.Field import scala.annotation.tailrec import scala.language.implicitConversions import scala.reflect.{ScalaLongSignature, ScalaSignature} @@ -28,8 +29,10 @@ trait ClassW extends PimpedType[Class[_]] { hasSigHelper(value) } - def isScalaObject: Boolean = { - Try(value.getField("MODULE$")).isSuccess + def isScalaObject: Boolean = getModuleField.nonEmpty + + def getModuleField: Option[Field] = { + Try(value.getField("MODULE$")).toOption } } From 7e5fea145551dd4083ca0229e537105b6daf65f1 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 30 Nov 2023 21:06:44 +0100 Subject: [PATCH 2/6] Update build.sbt --- build.sbt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 9ee4c4094..252eb2ee3 100644 --- a/build.sbt +++ b/build.sbt @@ -240,7 +240,8 @@ mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationKeyType"), ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationType"), ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationInclusionForContent"), - ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationInclusion") + ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationInclusion"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.util.ClassW.getModuleField") ) def compareVersions(version1: String, version2: String): Int = { From 6a3c6fe72704efd6ed181c4e879d9d229d71e219 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 30 Nov 2023 21:19:08 +0100 Subject: [PATCH 3/6] Update Classes.scala --- .../com/fasterxml/jackson/module/scala/util/Classes.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala index 5fff77042..818f86293 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala @@ -29,11 +29,11 @@ trait ClassW extends PimpedType[Class[_]] { hasSigHelper(value) } - def isScalaObject: Boolean = getModuleField.nonEmpty + def isScalaObject: Boolean = findModuleField.isSuccess - def getModuleField: Option[Field] = { - Try(value.getField("MODULE$")).toOption - } + def getModuleField: Option[Field] = findModuleField.toOption + + private def findModuleField: Try[Field] = Try(value.getField("MODULE$")) } object ClassW { From b3bf054fc624bfdfde1ed2fd8e2c08f76c66bb2d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 30 Nov 2023 21:34:57 +0100 Subject: [PATCH 4/6] refactor beanintrospector --- .../jackson/module/scala/introspect/BeanIntrospector.scala | 5 ++--- .../com/fasterxml/jackson/module/scala/util/Classes.scala | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/introspect/BeanIntrospector.scala b/src/main/scala/com/fasterxml/jackson/module/scala/introspect/BeanIntrospector.scala index ff77ce6b4..92855ae75 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/introspect/BeanIntrospector.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/introspect/BeanIntrospector.scala @@ -184,12 +184,11 @@ object BeanIntrospector { //create properties for all appropriate fields val fields = for { cls <- hierarchy - scalaCaseObject = isScalaCaseObject(cls) - isScalaObject = ClassW(cls).isScalaObject field <- cls.getDeclaredFields + isScalaObject = ClassW(cls).isScalaObject || isScalaCaseObject(cls) name = maybePrivateName(field) if !name.contains('$') - if (isScalaObject || scalaCaseObject || isAcceptableField(field)) + if isScalaObject || isAcceptableField(field) beanGetter = findBeanGetter(cls, name) beanSetter = findBeanSetter(cls, name) } yield PropertyDescriptor(name, findConstructorParam(hierarchy.head, name), Some(field), findGetter(cls, name), findSetter(cls, name), beanGetter, beanSetter) diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala b/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala index 818f86293..3452de3db 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/util/Classes.scala @@ -29,11 +29,11 @@ trait ClassW extends PimpedType[Class[_]] { hasSigHelper(value) } - def isScalaObject: Boolean = findModuleField.isSuccess + def isScalaObject: Boolean = moduleField.isSuccess - def getModuleField: Option[Field] = findModuleField.toOption + def getModuleField: Option[Field] = moduleField.toOption - private def findModuleField: Try[Field] = Try(value.getField("MODULE$")) + private lazy val moduleField: Try[Field] = Try(value.getField("MODULE$")) } object ClassW { From 5ab556fac6cd2d51ee3bff65a6ecd1626c53ca8f Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 30 Nov 2023 21:41:46 +0100 Subject: [PATCH 5/6] Update build.sbt --- build.sbt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 252eb2ee3..134509e26 100644 --- a/build.sbt +++ b/build.sbt @@ -241,7 +241,8 @@ mimaBinaryIssueFilters ++= Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationType"), ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationInclusionForContent"), ProblemFilters.exclude[DirectMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector.findSerializationInclusion"), - ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.util.ClassW.getModuleField") + ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.util.ClassW.getModuleField"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.util.ClassW.com$fasterxml$jackson$module$scala$util$ClassW$$moduleField") ) def compareVersions(version1: String, version2: String): Int = { From c11d0d157c9e26484b1f9526ca34ef6e23b3d9c9 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 1 Dec 2023 13:41:02 +0100 Subject: [PATCH 6/6] Update CaseObjectDeserializerTest.scala --- .../module/scala/deser/CaseObjectDeserializerTest.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/scala/com/fasterxml/jackson/module/scala/deser/CaseObjectDeserializerTest.scala b/src/test/scala/com/fasterxml/jackson/module/scala/deser/CaseObjectDeserializerTest.scala index 9f65e6814..b0c4cea3e 100644 --- a/src/test/scala/com/fasterxml/jackson/module/scala/deser/CaseObjectDeserializerTest.scala +++ b/src/test/scala/com/fasterxml/jackson/module/scala/deser/CaseObjectDeserializerTest.scala @@ -22,7 +22,7 @@ class CaseObjectDeserializerTest extends DeserializerTest { val original = TestObject val json = mapper.writeValueAsString(original) val deserialized = mapper.readValue(json, TestObject.getClass) - assert(deserialized == original) + assert(deserialized === original) } it should "deserialize Foo and not create a new instance" in { @@ -30,7 +30,7 @@ class CaseObjectDeserializerTest extends DeserializerTest { val original = Foo val json = mapper.writeValueAsString(original) val deserialized = mapper.readValue(json, Foo.getClass) - assert(deserialized == original) + assert(deserialized === original) } it should "deserialize Foo and not create a new instance (visibility settings)" in { @@ -42,7 +42,7 @@ class CaseObjectDeserializerTest extends DeserializerTest { val original = Foo val json = mapper.writeValueAsString(original) val deserialized = mapper.readValue(json, Foo.getClass) - assert(deserialized == original) + assert(deserialized === original) } "An ObjectMapper with ClassTagExtensions and DefaultScalaModule" should "deserialize a case object and not create a new instance" in { @@ -52,7 +52,7 @@ class CaseObjectDeserializerTest extends DeserializerTest { val original = TestObject val json = mapper.writeValueAsString(original) val deserialized = mapper.readValue[TestObject.type](json) - assert(deserialized == original) + assert(deserialized === original) } "An ObjectMapper without ScalaObjectDeserializerModule" should "deserialize a case object but create a new instance" in {