diff --git a/src/main/scala/com/fasterxml/jackson/module/scala/ser/MapSerializerModule.scala b/src/main/scala/com/fasterxml/jackson/module/scala/ser/MapSerializerModule.scala index 773076ea..a81cd363 100644 --- a/src/main/scala/com/fasterxml/jackson/module/scala/ser/MapSerializerModule.scala +++ b/src/main/scala/com/fasterxml/jackson/module/scala/ser/MapSerializerModule.scala @@ -8,20 +8,33 @@ import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer import com.fasterxml.jackson.databind.util.StdConverter import com.fasterxml.jackson.module.scala.modifiers.MapTypeModifierModule +import java.util import scala.collection.JavaConverters._ import scala.collection.Map private class MapConverter(inputType: JavaType, config: SerializationConfig) extends StdConverter[Map[_,_],java.util.Map[_,_]] { + // Making this an inner class avoids deserializaion errors when polymorphic typing + // is enabled. In Scala 2.12 `delegate.asJava` happened to be an inner class but + // this implementation detail changed in 2.13. + // + // Tested in DefaultTypingMapDeserializerTest + private class MapWrapper[A, B](delegate: Map[A, B]) extends util.AbstractMap[A, B] { + private val wrapped = delegate.asJava + + override def entrySet(): util.Set[util.Map.Entry[A, B]] = wrapped.entrySet() + } + def convert(value: Map[_,_]): java.util.Map[_,_] = { val m = if (config.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) { value } else { value.filter(_._2 != None) } - m.asJava - } + new MapWrapper(m) +// m.asJava + }`` override def getInputType(factory: TypeFactory) = inputType diff --git a/src/test/scala/com/fasterxml/jackson/module/scala/deser/DefaultTypingMapDeserializerTest.scala b/src/test/scala/com/fasterxml/jackson/module/scala/deser/DefaultTypingMapDeserializerTest.scala new file mode 100644 index 00000000..057ffe35 --- /dev/null +++ b/src/test/scala/com/fasterxml/jackson/module/scala/deser/DefaultTypingMapDeserializerTest.scala @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.module.scala.deser + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.scala.DefaultScalaModule + +import scala.collection.immutable + +class DefaultTypingMapDeserializerTest extends DeserializerTest { + + def module: DefaultScalaModule.type = DefaultScalaModule + + override def newMapper: ObjectMapper = { + val mapper = super.newMapper + mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator) + } + + "Scala Module" should "deserialize immutable Map when default typing enabled" in { + val map = HasMap(immutable.Map("one" -> "one", "two" -> "two")) + + val mapper = newMapper + + val json = mapper.writeValueAsString(map) + // Was failing in Scala 2.13+ with: + // > Could not resolve type id 'scala.collection.convert.JavaCollectionWrappers$MapWrapper' as a subtype of + // > `scala.collection.immutable.Map`: Not a subtype + // + // prior the changing MapSerializerModule.scala to use an inner class for MapWrapper + val read = mapper.readValue(json, classOf[HasMap]) + + read shouldEqual map + } + +} + +case class HasMap(m: Map[String, String])