【问题标题】:Jackson: (de)serialising Map with arbitrary non-string keyJackson:(使用任意非字符串键反序列化 Map
【发布时间】:2017-02-19 18:41:16
【问题描述】:

我有一个 Scala 映射,其键控类型本身需要序列化为 JSON。由于 JSON 的性质要求对象的键名是字符串,因此无法直接进行简单的映射。

我希望实现的解决方法是在序列化为 JSON 之前将 Map 转换为 Set,然后在反序列化后从 Set 转换回 Map。

我知道在 特定 类型上使用密钥序列化程序的其他方法,例如Serialising Map with Jackson,然而,我需要一个适用于任意键类型的解决方案,在这方面,转换为 Set 并再次返回在我看来是最好的选择。

通过从下面的jackson-module-scala 修改MapSerializerModule.scala,我已经成功地将带有包装对象的 Set 序列化为 Set,但我对 Jackson 内部结构不够熟悉,无法将该 JSON 反序列化回 Map I开始。

我应该补充一点,我同时控制序列化和反序列化,所以 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)
  }
}

【问题讨论】:

  • 我给你的答案是没有答案:注解和运行时反射是万恶之源。离开那个世界,转向基于类型类的方法

标签: json scala jackson jackson-module-scala


【解决方案1】:

我设法找到了解决方案:

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,因为案例类可以从字符串表示中重构。

【讨论】:

    【解决方案2】:

    在我的例子中,我有一个嵌套的 Map。这需要对转换回地图进行少量添加:

    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
      }
    }))
    

    【讨论】:

      猜你喜欢
      • 2019-10-31
      • 1970-01-01
      • 2018-03-17
      • 1970-01-01
      • 2019-11-23
      • 2015-10-25
      • 2013-12-05
      • 2011-10-13
      • 2012-11-04
      相关资源
      最近更新 更多