Skip to content

Commit e8d2846

Browse files
committed
wip
1 parent e737f4a commit e8d2846

File tree

3 files changed

+51
-35
lines changed

3 files changed

+51
-35
lines changed

src/main/scala/com/fasterxml/jackson/module/scala/introspect/BeanIntrospector.scala

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ import scala.reflect.NameTransformer
3737

3838
object BeanIntrospector {
3939

40-
private case class FieldKey(clazz: Class[_], fieldName: String)
41-
42-
private val overrideMap = scala.collection.mutable.Map[FieldKey, Class[_]]()
43-
4440
def apply[T <: AnyRef](cls: Class[_]) = {
4541

4642
/**
@@ -246,20 +242,6 @@ object BeanIntrospector {
246242
BeanDescriptor(cls, fields ++ methods ++ lazyValMethods)
247243
}
248244

249-
/**
250-
* jackson-module-scala does not always properly handle deserialization of Options or Collections wrapping
251-
* Scala primitives (eg Int, Long, Boolean). There are general issues with serializing and deserializing
252-
* Scala 2 Enumerations. These issues can be worked around by adding Jackson annotations on the affected fields.
253-
* This function is designed to be used when it is not possible to apply Jackson annotations.
254-
*
255-
* @param clazz
256-
* @param fieldName
257-
* @param referencedType
258-
*/
259-
def registerReferencedType(clazz: Class[_], fieldName: String, referencedType: Class[_]): Unit = {
260-
overrideMap.update(FieldKey(clazz, fieldName), referencedType)
261-
}
262-
263245
private def getCtorParams(ctor: Constructor[_]): Seq[String] = {
264246
val names = JavaParameterIntrospector.getCtorParamNames(ctor)
265247
names.map(NameTransformer.decode)

src/main/scala/com/fasterxml/jackson/module/scala/introspect/ScalaAnnotationIntrospectorModule.scala

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ object ScalaAnnotationIntrospector extends NopAnnotationIntrospector with ValueI
1616
private [this] var _descriptorCache: LookupCache[ClassKey, BeanDescriptor] =
1717
new LRUMap[ClassKey, BeanDescriptor](16, 100)
1818

19+
private case class FieldKey(clazz: Class[_], fieldName: String)
20+
21+
private val overrideMap = scala.collection.mutable.Map[FieldKey, Class[_]]()
22+
23+
/**
24+
* jackson-module-scala does not always properly handle deserialization of Options or Collections wrapping
25+
* Scala primitives (eg Int, Long, Boolean). There are general issues with serializing and deserializing
26+
* Scala 2 Enumerations. These issues can be worked around by adding Jackson annotations on the affected fields.
27+
* This function is designed to be used when it is not possible to apply Jackson annotations.
28+
*
29+
* @param clazz
30+
* @param fieldName
31+
* @param referencedType
32+
*/
33+
def registerReferencedType(clazz: Class[_], fieldName: String, referencedType: Class[_]): Unit = {
34+
overrideMap.update(FieldKey(clazz, fieldName), referencedType)
35+
}
36+
37+
def clearRegisteredReferencedTypes(): Unit = {
38+
overrideMap.clear()
39+
}
40+
1941
def setDescriptorCache(cache: LookupCache[ClassKey, BeanDescriptor]): LookupCache[ClassKey, BeanDescriptor] = {
2042
val existingCache = _descriptorCache
2143
_descriptorCache = cache
@@ -109,27 +131,33 @@ object ScalaAnnotationIntrospector extends NopAnnotationIntrospector with ValueI
109131
val applyDefaultValues = config.isEnabled(MapperFeature.APPLY_DEFAULT_VALUES)
110132
val args = delegate.getFromObjectArguments(config)
111133
Option(args) match {
112-
case Some(array) => {
134+
case Some(array) if (applyDefaultValues || overrideMap.nonEmpty) => {
113135
array.map {
114136
case creator: CreatorProperty => {
115-
if (applyDefaultValues) {
116-
// Locate the constructor param that matches it
117-
descriptor.properties.find(_.param.exists(_.index == creator.getCreatorIndex)) match {
118-
case Some(PropertyDescriptor(name, Some(ConstructorParameter(_, _, Some(defaultValue))), _, _, _, _, _)) =>
119-
creator.withNullProvider(new NullValueProvider {
120-
override def getNullValue(ctxt: DeserializationContext): AnyRef = defaultValue()
121-
122-
override def getNullAccessPattern: AccessPattern = AccessPattern.DYNAMIC
123-
})
124-
case _ => creator
137+
// Locate the constructor param that matches it
138+
descriptor.properties.find(_.param.exists(_.index == creator.getCreatorIndex)) match {
139+
case Some(pd) => {
140+
if (applyDefaultValues) {
141+
pd match {
142+
case PropertyDescriptor(_, Some(ConstructorParameter(_, _, Some(defaultValue))), _, _, _, _, _) => {
143+
creator.withNullProvider(new NullValueProvider {
144+
override def getNullValue(ctxt: DeserializationContext): AnyRef = defaultValue()
145+
146+
override def getNullAccessPattern: AccessPattern = AccessPattern.DYNAMIC
147+
})
148+
}
149+
case _ => creator
150+
}
151+
} else {
152+
creator
153+
}
125154
}
126-
} else {
127-
creator
155+
case _ => creator
128156
}
129157
}
130-
case other => other
131158
}
132159
}
160+
case Some(array) => array
133161
case _ => Array.empty
134162
}
135163
}

src/test/scala/com/fasterxml/jackson/module/scala/deser/OptionWithNumberDeserializerTest.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ package com.fasterxml.jackson.module.scala.deser
22

33
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
44
import com.fasterxml.jackson.module.scala.DefaultScalaModule
5-
import com.fasterxml.jackson.module.scala.introspect.BeanIntrospector
5+
import com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector
6+
import org.scalatest.BeforeAndAfterEach
67

78
object OptionWithNumberDeserializerTest {
89
case class AnnotatedOptionLong(@JsonDeserialize(contentAs = classOf[java.lang.Long]) valueLong: Option[Long])
@@ -12,14 +13,19 @@ object OptionWithNumberDeserializerTest {
1213
case class OptionBigInt(value: Option[BigInt])
1314
}
1415

15-
class OptionWithNumberDeserializerTest extends DeserializerTest {
16+
class OptionWithNumberDeserializerTest extends DeserializerTest with BeforeAndAfterEach {
1617
lazy val module: DefaultScalaModule.type = DefaultScalaModule
1718
import OptionWithNumberDeserializerTest._
1819

1920
private def useOptionLong(v: Option[Long]): Long = v.map(_ * 2).getOrElse(0L)
2021
private def useOptionJavaLong(v: Option[java.lang.Long]): Long = v.map(_ * 2).getOrElse(0L)
2122
private def useOptionBigInt(v: Option[BigInt]): Long = v.map(_ * 2).map(_.toLong).getOrElse(0L)
2223

24+
override def afterEach(): Unit = {
25+
super.afterEach()
26+
ScalaAnnotationIntrospector.clearRegisteredReferencedTypes()
27+
}
28+
2329
"JacksonModuleScala" should "support AnnotatedOptionLong" in {
2430
val v1 = deserialize("""{"valueLong":151}""", classOf[AnnotatedOptionLong])
2531
v1 shouldBe AnnotatedOptionLong(Some(151L))
@@ -35,7 +41,7 @@ class OptionWithNumberDeserializerTest extends DeserializerTest {
3541
}
3642

3743
it should "support OptionLong" in {
38-
BeanIntrospector.registerReferencedType(classOf[OptionLong], "valueLong", classOf[Long])
44+
ScalaAnnotationIntrospector.registerReferencedType(classOf[OptionLong], "valueLong", classOf[Long])
3945
val v1 = deserialize("""{"valueLong":151}""", classOf[OptionLong])
4046
v1 shouldBe OptionLong(Some(151L))
4147
v1.valueLong.get shouldBe 151L

0 commit comments

Comments
 (0)