Skip to content

Commit 31d1c3c

Browse files
authored
Int/long map deserialization (#595)
1 parent 6b29ed0 commit 31d1c3c

File tree

8 files changed

+488
-3
lines changed

8 files changed

+488
-3
lines changed

src/main/scala-2.11/com/fasterxml/jackson/module/scala/deser/UnsortedMapDeserializerModule.scala

+22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.fasterxml.jackson.module.scala.deser
22

33
import com.fasterxml.jackson.databind._
4+
import com.fasterxml.jackson.databind.`type`.MapLikeType
5+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer
46
import com.fasterxml.jackson.module.scala.modifiers.MapTypeModifierModule
57

68
import scala.collection._
@@ -27,5 +29,25 @@ trait UnsortedMapDeserializerModule extends MapTypeModifierModule {
2729
))
2830

2931
override def builderFor[K, V](factory: Factory, keyType: JavaType, valueType: JavaType): Builder[K, V] = factory.newBuilder[K, V]
32+
33+
override def findMapLikeDeserializer(theType: MapLikeType,
34+
config: DeserializationConfig,
35+
beanDesc: BeanDescription,
36+
keyDeserializer: KeyDeserializer,
37+
elementTypeDeserializer: TypeDeserializer,
38+
elementDeserializer: JsonDeserializer[_]): JsonDeserializer[_] = {
39+
40+
var deserializer = LongMapDeserializerResolver.findMapLikeDeserializer(
41+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
42+
if (deserializer == null) {
43+
deserializer = IntMapDeserializerResolver.findMapLikeDeserializer(
44+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
45+
if (deserializer == null) {
46+
deserializer = super.findMapLikeDeserializer(
47+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
48+
}
49+
}
50+
deserializer
51+
}
3052
})
3153
}

src/main/scala-2.12/com/fasterxml/jackson/module/scala/deser/UnsortedMapDeserializerModule.scala

+22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.fasterxml.jackson.module.scala.deser
22

33
import com.fasterxml.jackson.databind._
4+
import com.fasterxml.jackson.databind.`type`.MapLikeType
5+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer
46
import com.fasterxml.jackson.module.scala.modifiers.MapTypeModifierModule
57

68
import scala.collection._
@@ -27,5 +29,25 @@ trait UnsortedMapDeserializerModule extends MapTypeModifierModule {
2729
))
2830

2931
override def builderFor[K, V](factory: Factory, keyType: JavaType, valueType: JavaType): Builder[K, V] = factory.newBuilder[K, V]
32+
33+
override def findMapLikeDeserializer(theType: MapLikeType,
34+
config: DeserializationConfig,
35+
beanDesc: BeanDescription,
36+
keyDeserializer: KeyDeserializer,
37+
elementTypeDeserializer: TypeDeserializer,
38+
elementDeserializer: JsonDeserializer[_]): JsonDeserializer[_] = {
39+
40+
var deserializer = LongMapDeserializerResolver.findMapLikeDeserializer(
41+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
42+
if (deserializer == null) {
43+
deserializer = IntMapDeserializerResolver.findMapLikeDeserializer(
44+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
45+
if (deserializer == null) {
46+
deserializer = super.findMapLikeDeserializer(
47+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
48+
}
49+
}
50+
deserializer
51+
}
3052
})
3153
}

src/main/scala-2.13/com/fasterxml/jackson/module/scala/deser/UnsortedMapDeserializerModule.scala

+22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.fasterxml.jackson.module.scala.deser
22

33
import com.fasterxml.jackson.databind._
4+
import com.fasterxml.jackson.databind.`type`.MapLikeType
5+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer
46
import com.fasterxml.jackson.module.scala.modifiers.MapTypeModifierModule
57

68
import scala.collection._
@@ -26,5 +28,25 @@ trait UnsortedMapDeserializerModule extends MapTypeModifierModule {
2628
))
2729

2830
override def builderFor[K, V](factory: Factory, keyType: JavaType, valueType: JavaType): Builder[K, V] = factory.newBuilder[K, V]
31+
32+
override def findMapLikeDeserializer(theType: MapLikeType,
33+
config: DeserializationConfig,
34+
beanDesc: BeanDescription,
35+
keyDeserializer: KeyDeserializer,
36+
elementTypeDeserializer: TypeDeserializer,
37+
elementDeserializer: JsonDeserializer[_]): JsonDeserializer[_] = {
38+
39+
var deserializer = LongMapDeserializerResolver.findMapLikeDeserializer(
40+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
41+
if (deserializer == null) {
42+
deserializer = IntMapDeserializerResolver.findMapLikeDeserializer(
43+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
44+
if (deserializer == null) {
45+
deserializer = super.findMapLikeDeserializer(
46+
theType, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer)
47+
}
48+
}
49+
deserializer
50+
}
2951
})
3052
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.fasterxml.jackson.module.scala.deser
2+
3+
import com.fasterxml.jackson.core.JsonParser
4+
import com.fasterxml.jackson.databind.`type`.MapLikeType
5+
import com.fasterxml.jackson.databind.deser.{ContextualDeserializer, Deserializers}
6+
import com.fasterxml.jackson.databind.deser.std.{ContainerDeserializerBase, MapDeserializer, StdValueInstantiator}
7+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer
8+
import com.fasterxml.jackson.databind._
9+
import com.fasterxml.jackson.module.scala.{DefaultScalaModule, IteratorModule}
10+
11+
import java.util
12+
import scala.collection.JavaConverters._
13+
import scala.collection.immutable.IntMap
14+
15+
/**
16+
* Adds support for deserializing Scala [[scala.collection.immutable.IntMap]]s. Scala IntMaps can already be
17+
* serialized using [[IteratorModule]] or [[DefaultScalaModule]].
18+
*
19+
* @since 2.14.0
20+
*/
21+
private[deser] object IntMapDeserializerResolver extends Deserializers.Base {
22+
23+
private val intMapClass = classOf[IntMap[_]]
24+
25+
override def findMapLikeDeserializer(theType: MapLikeType,
26+
config: DeserializationConfig,
27+
beanDesc: BeanDescription,
28+
keyDeserializer: KeyDeserializer,
29+
elementTypeDeserializer: TypeDeserializer,
30+
elementDeserializer: JsonDeserializer[_]): JsonDeserializer[_] = {
31+
if (!intMapClass.isAssignableFrom(theType.getRawClass)) None.orNull
32+
else {
33+
val mapDeserializer = new MapDeserializer(theType, new IntMapInstantiator(config, theType), keyDeserializer,
34+
elementDeserializer.asInstanceOf[JsonDeserializer[AnyRef]], elementTypeDeserializer)
35+
new IntMapDeserializer(theType, mapDeserializer)
36+
}
37+
}
38+
39+
private class IntMapDeserializer[V](mapType: MapLikeType, containerDeserializer: MapDeserializer)
40+
extends ContainerDeserializerBase[IntMap[V]](mapType) with ContextualDeserializer {
41+
42+
override def getContentType: JavaType = containerDeserializer.getContentType
43+
44+
override def getContentDeserializer: JsonDeserializer[AnyRef] = containerDeserializer.getContentDeserializer
45+
46+
override def createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer[_] = {
47+
val newDelegate = containerDeserializer.createContextual(ctxt, property).asInstanceOf[MapDeserializer]
48+
new IntMapDeserializer(mapType, newDelegate)
49+
}
50+
51+
override def deserialize(jp: JsonParser, ctxt: DeserializationContext): IntMap[V] = {
52+
containerDeserializer.deserialize(jp, ctxt) match {
53+
case wrapper: BuilderWrapper => wrapper.asIntMap()
54+
}
55+
}
56+
57+
override def deserialize(jp: JsonParser, ctxt: DeserializationContext, intoValue: IntMap[V]): IntMap[V] = {
58+
val newMap = deserialize(jp, ctxt)
59+
if (newMap.isEmpty) {
60+
intoValue
61+
} else {
62+
intoValue ++ newMap
63+
}
64+
}
65+
66+
override def getEmptyValue(ctxt: DeserializationContext): Object = IntMap.empty[V]
67+
}
68+
69+
private class IntMapInstantiator(config: DeserializationConfig, mapType: MapLikeType) extends StdValueInstantiator(config, mapType) {
70+
override def canCreateUsingDefault = true
71+
override def createUsingDefault(ctxt: DeserializationContext) = new BuilderWrapper
72+
}
73+
74+
private class BuilderWrapper extends util.AbstractMap[Object, Object] {
75+
var baseMap = IntMap[Object]()
76+
77+
override def put(k: Object, v: Object): Object = {
78+
k match {
79+
case n: Number => baseMap += (n.intValue() -> v)
80+
case s: String => baseMap += (s.toInt -> v)
81+
case _ => {
82+
val typeName = Option(k) match {
83+
case Some(n) => n.getClass.getName
84+
case _ => "null"
85+
}
86+
throw new IllegalArgumentException(s"IntMap does npt support keys of type $typeName")
87+
}
88+
}
89+
v
90+
}
91+
92+
// Used by the deserializer when using readerForUpdating
93+
override def get(key: Object): Object = key match {
94+
case n: Number => baseMap.get(n.intValue()).orNull
95+
case s: String => baseMap.get(s.toInt).orNull
96+
case _ => None.orNull
97+
}
98+
99+
// Isn't used by the deserializer
100+
override def entrySet(): java.util.Set[java.util.Map.Entry[Object, Object]] =
101+
baseMap.asJava.entrySet().asInstanceOf[java.util.Set[java.util.Map.Entry[Object, Object]]]
102+
103+
def asIntMap[V](): IntMap[V] = baseMap.asInstanceOf[IntMap[V]]
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package com.fasterxml.jackson.module.scala.deser
2+
3+
import com.fasterxml.jackson.core.JsonParser
4+
import com.fasterxml.jackson.databind.`type`.MapLikeType
5+
import com.fasterxml.jackson.databind.deser.{ContextualDeserializer, Deserializers}
6+
import com.fasterxml.jackson.databind.deser.std.{ContainerDeserializerBase, MapDeserializer, StdValueInstantiator}
7+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer
8+
import com.fasterxml.jackson.databind._
9+
import com.fasterxml.jackson.module.scala.{DefaultScalaModule, IteratorModule}
10+
11+
import java.util
12+
import scala.collection.{immutable, mutable}
13+
import scala.collection.JavaConverters._
14+
15+
/**
16+
* Adds support for deserializing Scala [[scala.collection.immutable.LongMap]]s and [[scala.collection.mutable.LongMap]]s.
17+
* Scala LongMaps can already be serialized using [[IteratorModule]] or [[DefaultScalaModule]].
18+
*
19+
* @since 2.14.0
20+
*/
21+
private[deser] object LongMapDeserializerResolver extends Deserializers.Base {
22+
23+
private val immutableLongMapClass = classOf[immutable.LongMap[_]]
24+
private val mutableLongMapClass = classOf[mutable.LongMap[_]]
25+
26+
override def findMapLikeDeserializer(theType: MapLikeType,
27+
config: DeserializationConfig,
28+
beanDesc: BeanDescription,
29+
keyDeserializer: KeyDeserializer,
30+
elementTypeDeserializer: TypeDeserializer,
31+
elementDeserializer: JsonDeserializer[_]): JsonDeserializer[_] = {
32+
if (immutableLongMapClass.isAssignableFrom(theType.getRawClass)) {
33+
val mapDeserializer = new MapDeserializer(theType, new ImmutableLongMapInstantiator(config, theType), keyDeserializer,
34+
elementDeserializer.asInstanceOf[JsonDeserializer[AnyRef]], elementTypeDeserializer)
35+
new ImmutableLongMapDeserializer(theType, mapDeserializer)
36+
} else if (mutableLongMapClass.isAssignableFrom(theType.getRawClass)) {
37+
val mapDeserializer = new MapDeserializer(theType, new MutableLongMapInstantiator(config, theType), keyDeserializer,
38+
elementDeserializer.asInstanceOf[JsonDeserializer[AnyRef]], elementTypeDeserializer)
39+
new MutableLongMapDeserializer(theType, mapDeserializer)
40+
} else {
41+
None.orNull
42+
}
43+
}
44+
45+
private class ImmutableLongMapDeserializer[V](mapType: MapLikeType, containerDeserializer: MapDeserializer)
46+
extends ContainerDeserializerBase[immutable.LongMap[V]](mapType) with ContextualDeserializer {
47+
48+
override def getContentType: JavaType = containerDeserializer.getContentType
49+
50+
override def getContentDeserializer: JsonDeserializer[AnyRef] = containerDeserializer.getContentDeserializer
51+
52+
override def createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer[_] = {
53+
val newDelegate = containerDeserializer.createContextual(ctxt, property).asInstanceOf[MapDeserializer]
54+
new ImmutableLongMapDeserializer(mapType, newDelegate)
55+
}
56+
57+
override def deserialize(jp: JsonParser, ctxt: DeserializationContext): immutable.LongMap[V] = {
58+
containerDeserializer.deserialize(jp, ctxt) match {
59+
case wrapper: ImmutableMapWrapper => wrapper.asLongMap()
60+
}
61+
}
62+
63+
override def deserialize(jp: JsonParser, ctxt: DeserializationContext, intoValue: immutable.LongMap[V]): immutable.LongMap[V] = {
64+
val newMap = deserialize(jp, ctxt)
65+
if (newMap.isEmpty) {
66+
intoValue
67+
} else {
68+
intoValue ++ newMap
69+
}
70+
}
71+
72+
override def getEmptyValue(ctxt: DeserializationContext): Object = immutable.LongMap.empty[V]
73+
}
74+
75+
private class MutableLongMapDeserializer[V](mapType: MapLikeType, containerDeserializer: MapDeserializer)
76+
extends ContainerDeserializerBase[mutable.LongMap[V]](mapType) with ContextualDeserializer {
77+
78+
override def getContentType: JavaType = containerDeserializer.getContentType
79+
80+
override def getContentDeserializer: JsonDeserializer[AnyRef] = containerDeserializer.getContentDeserializer
81+
82+
override def createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer[_] = {
83+
val newDelegate = containerDeserializer.createContextual(ctxt, property).asInstanceOf[MapDeserializer]
84+
new MutableLongMapDeserializer(mapType, newDelegate)
85+
}
86+
87+
override def deserialize(jp: JsonParser, ctxt: DeserializationContext): mutable.LongMap[V] = {
88+
containerDeserializer.deserialize(jp, ctxt) match {
89+
case wrapper: MutableMapWrapper => wrapper.asLongMap()
90+
}
91+
}
92+
93+
override def deserialize(jp: JsonParser, ctxt: DeserializationContext, intoValue: mutable.LongMap[V]): mutable.LongMap[V] = {
94+
val newMap = deserialize(jp, ctxt)
95+
if (newMap.isEmpty) {
96+
intoValue
97+
} else {
98+
intoValue ++ newMap
99+
}
100+
}
101+
102+
override def getEmptyValue(ctxt: DeserializationContext): Object = mutable.LongMap.empty[V]
103+
}
104+
105+
private class ImmutableLongMapInstantiator(config: DeserializationConfig, mapType: MapLikeType) extends StdValueInstantiator(config, mapType) {
106+
override def canCreateUsingDefault = true
107+
override def createUsingDefault(ctxt: DeserializationContext) = new ImmutableMapWrapper
108+
}
109+
110+
private class MutableLongMapInstantiator(config: DeserializationConfig, mapType: MapLikeType) extends StdValueInstantiator(config, mapType) {
111+
override def canCreateUsingDefault = true
112+
113+
override def createUsingDefault(ctxt: DeserializationContext) = new MutableMapWrapper
114+
}
115+
116+
private class ImmutableMapWrapper extends util.AbstractMap[Object, Object] {
117+
var baseMap = immutable.LongMap[Object]()
118+
119+
override def put(k: Object, v: Object): Object = {
120+
k match {
121+
case n: Number => baseMap += (n.longValue() -> v)
122+
case s: String => baseMap += (s.toLong -> v)
123+
case _ => {
124+
val typeName = Option(k) match {
125+
case Some(n) => n.getClass.getName
126+
case _ => "null"
127+
}
128+
throw new IllegalArgumentException(s"IntMap does npt support keys of type $typeName")
129+
}
130+
}
131+
v
132+
}
133+
134+
// Used by the deserializer when using readerForUpdating
135+
override def get(key: Object): Object = key match {
136+
case n: Number => baseMap.get(n.longValue()).orNull
137+
case s: String => baseMap.get(s.toInt).orNull
138+
case _ => None.orNull
139+
}
140+
141+
// Isn't used by the deserializer
142+
override def entrySet(): java.util.Set[java.util.Map.Entry[Object, Object]] =
143+
baseMap.asJava.entrySet().asInstanceOf[java.util.Set[java.util.Map.Entry[Object, Object]]]
144+
145+
def asLongMap[V](): immutable.LongMap[V] = baseMap.asInstanceOf[immutable.LongMap[V]]
146+
}
147+
148+
private class MutableMapWrapper extends util.AbstractMap[Object, Object] {
149+
var baseMap = mutable.LongMap[Object]()
150+
151+
override def put(k: Object, v: Object): Object = {
152+
k match {
153+
case n: Number => baseMap += (n.longValue() -> v)
154+
case s: String => baseMap += (s.toLong -> v)
155+
case _ => {
156+
val typeName = Option(k) match {
157+
case Some(n) => n.getClass.getName
158+
case _ => "null"
159+
}
160+
throw new IllegalArgumentException(s"IntMap does npt support keys of type $typeName")
161+
}
162+
}
163+
v
164+
}
165+
166+
// Used by the deserializer when using readerForUpdating
167+
override def get(key: Object): Object = key match {
168+
case n: Number => baseMap.get(n.longValue()).orNull
169+
case s: String => baseMap.get(s.toInt).orNull
170+
case _ => None.orNull
171+
}
172+
173+
// Isn't used by the deserializer
174+
override def entrySet(): java.util.Set[java.util.Map.Entry[Object, Object]] =
175+
baseMap.asJava.entrySet().asInstanceOf[java.util.Set[java.util.Map.Entry[Object, Object]]]
176+
177+
def asLongMap[V](): mutable.LongMap[V] = baseMap.asInstanceOf[mutable.LongMap[V]]
178+
}
179+
180+
}

0 commit comments

Comments
 (0)