Skip to content

Commit 981db7f

Browse files
committed
Fix regression in Map deserializtion in Scala 2.13 when default typing is enabled.
This Map serializer is implemented in terms of the Java version and is somewhat fragile, as discussed in FasterXML#643 / FasterXML#470. This commit ensures that the wrapper class is a member class in both Scala and Java. That results in this JSON as the serializer code chooses not to put the inner class name in the type annotation. ``` {"m":["scala.collection.immutable.Map",{"one":"one","two":"two"}]} ``` Fixes FasterXML#643
1 parent 020e558 commit 981db7f

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

src/main/scala/com/fasterxml/jackson/module/scala/ser/MapSerializerModule.scala

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,33 @@ import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer
88
import com.fasterxml.jackson.databind.util.StdConverter
99
import com.fasterxml.jackson.module.scala.modifiers.MapTypeModifierModule
1010

11+
import java.util
1112
import scala.collection.JavaConverters._
1213
import scala.collection.Map
1314

1415
private class MapConverter(inputType: JavaType, config: SerializationConfig)
1516
extends StdConverter[Map[_,_],java.util.Map[_,_]]
1617
{
18+
// Making this an inner class avoids deserializaion errors when polymorphic typing
19+
// is enabled. In Scala 2.12 `delegate.asJava` happened to be an inner class but
20+
// this implementation detail changed in 2.13.
21+
//
22+
// Tested in DefaultTypingMapDeserializerTest
23+
private class MapWrapper[A, B](delegate: Map[A, B]) extends util.AbstractMap[A, B] {
24+
private val wrapped = delegate.asJava
25+
26+
override def entrySet(): util.Set[util.Map.Entry[A, B]] = wrapped.entrySet()
27+
}
28+
1729
def convert(value: Map[_,_]): java.util.Map[_,_] = {
1830
val m = if (config.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
1931
value
2032
} else {
2133
value.filter(_._2 != None)
2234
}
23-
m.asJava
24-
}
35+
new MapWrapper(m)
36+
// m.asJava
37+
}``
2538

2639

2740
override def getInputType(factory: TypeFactory) = inputType
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.fasterxml.jackson.module.scala.deser
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper
4+
import com.fasterxml.jackson.module.scala.DefaultScalaModule
5+
6+
import scala.collection.immutable
7+
8+
class DefaultTypingMapDeserializerTest extends DeserializerTest {
9+
10+
def module: DefaultScalaModule.type = DefaultScalaModule
11+
12+
override def newMapper: ObjectMapper = {
13+
val mapper = super.newMapper
14+
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator)
15+
}
16+
17+
"Scala Module" should "deserialize immutable Map when default typing enabled" in {
18+
val map = HasMap(immutable.Map("one" -> "one", "two" -> "two"))
19+
20+
val mapper = newMapper
21+
22+
val json = mapper.writeValueAsString(map)
23+
// Was failing in Scala 2.13+ with:
24+
// > Could not resolve type id 'scala.collection.convert.JavaCollectionWrappers$MapWrapper' as a subtype of
25+
// > `scala.collection.immutable.Map<java.lang.String,java.lang.String>`: Not a subtype
26+
//
27+
// prior the changing MapSerializerModule.scala to use an inner class for MapWrapper
28+
val read = mapper.readValue(json, classOf[HasMap])
29+
30+
read shouldEqual map
31+
}
32+
33+
}
34+
35+
case class HasMap(m: Map[String, String])

0 commit comments

Comments
 (0)