我有一个Scala Map键入一个本身需要序列化为JSON的类型。由于JSON的性质要求对象的键名是字符串,因此不能直接进行简单的映射。
我希望实现的工作是在序列化为JSON之前将Map转换为Set,然后在反序列化之后从Set转换为Map。
我知道在特定类型上使用密钥序列化器的其他方法,例如Serialising Map with Jackson但是,我需要一个适用于任意键类型的解决方案,在这方面,转换为Set并再次返回看起来像是最好的选择。
通过修改下面MapSerializerModule.scala的jackson-module-scala,我已经成功地使用包装器对象序列化了一些成功,但我对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 :(得分: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
不是必需的,因为案例类可以从字符串表示重构。
答案 1 :(得分:0)
就我而言,我有一个嵌套的地图。这需要对转换回地图进行少量补充:
addDeserializer(classOf[Map[_, _]], new StdDelegatingDeserializer(new StdConverter[MapWrapper[_, _], Map[_, _]] {
def convert(mapWrapper: MapWrapper[_, _]): Map[_, _] = {
mapWrapper.wrappedMap.map { case MapEntry(k, v) => {
v match {
case wm: MapWrapper[_, _] => (k, convert(wm))
case _ => (k, v)
}
}}.toMap
}
}))