Skip to content

Commit 407d743

Browse files
authored
Fix regression in Map deser, in Scala 2.13 w default typing (#658)
This Map serializer is implemented in terms of the Java version and is somewhat fragile, as discussed in #643 / #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 #643
1 parent 020e558 commit 407d743

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,24 @@ import scala.collection.Map
1414
private class MapConverter(inputType: JavaType, config: SerializationConfig)
1515
extends StdConverter[Map[_,_],java.util.Map[_,_]]
1616
{
17+
// Making this an inner class avoids deserializaion errors when polymorphic typing
18+
// is enabled. In Scala 2.12 `delegate.asJava` happened to be an inner class but
19+
// this implementation detail changed in 2.13.
20+
//
21+
// Tested in DefaultTypingMapDeserializerTest
22+
private class MapWrapper[A, B](delegate: Map[A, B]) extends java.util.AbstractMap[A, B] {
23+
private val wrapped = delegate.asJava
24+
25+
override def entrySet(): java.util.Set[java.util.Map.Entry[A, B]] = wrapped.entrySet()
26+
}
27+
1728
def convert(value: Map[_,_]): java.util.Map[_,_] = {
1829
val m = if (config.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) {
1930
value
2031
} else {
2132
value.filter(_._2 != None)
2233
}
23-
m.asJava
34+
new MapWrapper(m)
2435
}
2536

2637

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)