2017-02-19 15 views
0

JSONにシリアライズする必要がある型のキーを持つスカラマップがあります。オブジェクトの文字列にキー名を必要とするJSONの性質のため、単純なマッピングは直接できません。ジャクソン:任意の非文字列キーを使用した(デ)シリアライズマップ

実装したいのは、マップをJSONにシリアル化する前にセットに変換してから、デシリアライズ後にセットからマップに変換することです。

私は、のキーシリアルシリアを使用して他の方法を知っています。タイプです。 Serialising Map with Jacksonしかし、私は任意のキーの種類に適用されるソリューションが必要です。この点に関しては、セットとバックへの変換は、私には最良の選択肢のように見えます。

MapSerializerModule.scalaを下記のjackson-module-scalaから修正して、ラッパーオブジェクトを使ってSetにシリアライズすることができましたが、JSONを使用して始めたマップにデシリアライズするためには、

シリアライズとデシリアライゼーションの両方をコントロールする必要があるので、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は必要ありません。

関連する問題