2017-02-19 19 views
0

我有一個本身需要序列化爲JSON的類型鍵入的Scala Map。由於JSON的特性需要對象的鍵名稱爲字符串,因此不能直接進行簡單的映射。傑克遜:(de)序列化使用任意非字符串鍵的地圖

我希望實現的工作是在將序列化爲JSON之前將映射轉換爲集合,然後在反序列化之後將集合轉換爲映射。

我知道使用特定類型的鍵串行器的其他方法,例如,但是,我需要一個適用於任意鍵類型的解決方案,並且在這方面,轉換爲Set並且再次回到我看來是最好的選擇。

我已經通過修改MapSerializerModule.scala下面的jackson-module-scala序列化了一個包裝對象Set,但是我對Jackson內部不熟悉,無法讓JSON去反序列化到我開始的Map。

我應該補充一點,我控制了序列化和反序列化兩方面,所以JSON看起來並不重要。

case class Wrapper[K, V](
    value: Set[(K, V)] 
) 

class MapConverter[K, V](inputType: JavaType, config: SerializationConfig) 
    extends StdConverter[Map[K, V], Wrapper[K, V]] { 
    def convert(value: Map[K, V]): Wrapper[K, V] = { 
    val set = value.toSet 
    Wrapper(set) 
    } 

    override def getInputType(factory: TypeFactory) = inputType 

    override def getOutputType(factory: TypeFactory) = 
    factory.constructReferenceType(classOf[Wrapper[_, _]], inputType.getContentType) 
     .withTypeHandler(inputType.getTypeHandler) 
     .withValueHandler(inputType.getValueHandler) 
} 

object MapSerializerResolver extends Serializers.Base { 

    val MAP = classOf[Map[_, _]] 

    override def findMapLikeSerializer(
    config: SerializationConfig, 
    typ: MapLikeType, 
    beanDesc: BeanDescription, 
    keySerializer: JsonSerializer[AnyRef], 
    elementTypeSerializer: TypeSerializer, 
    elementValueSerializer: JsonSerializer[AnyRef]): JsonSerializer[_] = { 

    val rawClass = typ.getRawClass 

    if (!MAP.isAssignableFrom(rawClass)) null 
    else new StdDelegatingSerializer(new MapConverter(typ, config)) 
    } 
} 

object Main { 
    def main(args: Array[String]): Unit = { 
    val objMap = Map(
     new Key("k1", "k2") -> "k1k2", 
     new Key("k2", "k3") -> "k2k3") 

    val om = new ObjectMapper() 
    om.registerModule(DefaultScalaModule) 
    om.registerModule(ConverterModule) 

    val res = om.writeValueAsString(objMap) 
    println(res) 
    } 
} 
+0

我給你一個答案,這是一個沒有答案:註釋和運行時反射是所有邪惡的源泉。離開這個世界並轉向基於類型類的方法 – Edmondo1984

回答

0

我設法找到了解決方案:

case class MapWrapper[K, V](
    wrappedMap: Set[MapEntry[K, V]] 
) 

case class MapEntry[K, V](
    key: K, 
    value: V 
) 

object MapConverter extends SimpleModule { 
    addSerializer(classOf[Map[_, _]], new StdDelegatingSerializer(new StdConverter[Map[_, _], MapWrapper[_, _]] { 
    def convert(inMap: Map[_, _]): MapWrapper[_, _] = MapWrapper(inMap map { case (k, v) => MapEntry(k, v) } toSet) 
    })) 

    addDeserializer(classOf[Map[_, _]], new StdDelegatingDeserializer(new StdConverter[MapWrapper[_, _], Map[_, _]] { 
    def convert(mapWrapper: MapWrapper[_, _]): Map[_, _] = mapWrapper.wrappedMap map { case MapEntry(k, v) => (k, v) } toMap 
    })) 
} 

class MapKey(
    val k1: String, 
    val k2: String 
) { 
    override def toString: String = s"MapKey($k1, $k2) (str)" 
} 


object Main { 
    def main(args: Array[String]): Unit = { 
    val objMap = Map(
     new MapKey("k1", "k2") -> "k1k2", 
     new MapKey("k2", "k3") -> "k2k3") 

    val om = setupObjectMapper 

    val jsonMap = om.writeValueAsString(objMap) 

    val deserMap = om.readValue(jsonMap, classOf[Map[_, _]]) 
    } 

    private def setupObjectMapper = { 
    val typeResolverBuilder = 
     new DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL) { 
     init(JsonTypeInfo.Id.CLASS, null) 
     inclusion(JsonTypeInfo.As.WRAPPER_OBJECT) 
     typeProperty("@CLASS") 

     override def useForType(t: JavaType): Boolean = !t.isContainerType && super.useForType(t) 
     } 

    val om = new ObjectMapper() 
    om.registerModule(DefaultScalaModule) 
    om.registerModule(MapConverter) 
    om.setDefaultTyping(typeResolverBuilder) 

    om 
    } 
} 

有趣的是,如果密鑰類型是的情況下類中,MapConverter是不必要的,因爲殼體類可以從字符串表示來重構。