【问题标题】:Jackson Scala Module, nested Lists and MapsJackson Scala 模块,嵌套列表和地图
【发布时间】:2012-01-09 18:00:38
【问题描述】:

我希望转换 json 对象列表,这些对象本身可能包含 Json 对象或 Json 对象列表,并且希望结果是适当的 Scala 映射或列表,但是,我只能将顶级对象转换为转换为地图或列表。 有没有简单的方法来做到这一点?

例如在 REPL 中

objectMapper.readValue("{\"test\":\"113123\",\"myList\":[{\"test2\":\"321323\"},{\"test3\":\"11122\"}]}", classOf[Map[String,Any]])

会回来

res: Map[String,Any] = Map(test -> 113123, myList -> [{test2=321323}, {test3=11122}])

我想去哪里

res: Map[String,Any] = Map(test -> 113123, myList -> List(Map(test2 -> 321323), Map(test3 -> 111222)))

【问题讨论】:

  • 你真的需要使用Jackson吗?将 lift-json 与案例类一起使用要简单得多。
  • 如果映射有效,那么使用 Jackson 数据绑定就没有什么复杂的了:lift-json 怎么会比上面显示的一个衬垫更简单?
  • 我用过 lift 和 Jackson,发现 lift 在处理 15,000 多个 json 对象时出现内存问题,而 Jackson 处理得很好
  • 我构建了对象映射器以与 mapModule 和 seqModule 一起使用
  • 我还没有尝试 jerkson,有可用的 jar 吗?(我是 jvm 的新手,还没有从 github 解决构建 jar..)

标签: json scala nested jackson


【解决方案1】:

好的,所以我使用更高版本的 scala jackson 模块创建了这个类

private class NestedTypeObjectDeserializer extends JacksonUntypedObjectDeserializer {

  override def mapArray(jp: JsonParser, ctxt: DeserializationContext): AnyRef =
    super.mapArray(jp, ctxt).asInstanceOf[ArrayList[AnyRef]].asScala.toList

  override def mapObject(jp: JsonParser, ctxt: DeserializationContext): AnyRef =
    super.mapObject(jp, ctxt).asInstanceOf[LinkedHashMap[String, AnyRef]].asScala.toMap
}


private object NestedTypeObjectDeserializerResolver extends Deserializers.Base {

  lazy val OBJECT = classOf[AnyRef]

  override def findBeanDeserializer(javaType: JavaType,
                                    config: DeserializationConfig,
                                    beanDesc: BeanDescription) =
    if (!OBJECT.equals(javaType.getRawClass)) null
    else new NestedTypeObjectDeserializer
}

trait NestedTypeObjectDeserializerModule extends JacksonModule {
  this += (_ addDeserializers NestedTypeObjectDeserializerResolver)
}

您可以将其应用于对象映射器

val objectMapper = new ObjectMapper()
val module = new OptionModule with MapModule with SeqModule with IteratorModule with NestedTypeObjectDeserializerModule {}
objectMapper.registerModule(module)

【讨论】:

  • 我遇到了同样的问题,但发现如果我没有注册 UntypedObjectDeserializer,它会像我最初预期的那样工作。
【解决方案2】:

如果这不能按预期工作,请在 [https://github.com/FasterXML/jackson-module-scala/issues] 上针对 Jackson Scala 模块提出问题。在“无类型”模式下处理内容可能存在问题(即非 bean,使用列表、地图等),scala 模块需要为其添加特殊处理。潜在的挑战是 Scala 集合通常不能与 JDK 集合完全映射。

【讨论】:

  • 好,希望它快点得到地址!
  • 这个问题已经修复,创建了新的答案来显示你需要使用的代码
【解决方案3】:

我没有使用 Jackson Scala 模块,但 Jerkson 中的行为是相同的,所以我会冒险猜测发生了什么。 REPL 打印时:

myList -> [{test2=321323}, {test3=11122}])

它告诉你有一个包含“myList”和一个 java.util.ArrayList 的元组。 ArrayList 包含两个 java.util.LinkedHashMaps。 (我之所以解释这一点,是因为一开始对我来说并不明显,我不得不花一些时间使用 REPL 使用 getClass 和 asInstanceOf 来深入研究该结构。)

我相当肯定没有办法让 Jackson Scala Module 或 Jerkson 返回带有原生 Scala 集合的反序列化 JSON。您可以导入 scala.collect.JavaConversions 以使这些值看起来更像 Scala。即使你这样做了,你仍然需要到处乱扔东西。例如:

import com.codahale.jerkson.Json._
import collection.JavaConversions._
import java.util._
res("myList").asInstanceOf[ArrayList[LinkedHashMap[String,String]]](0)("test2")
  >> String = 321323

如果您不想摆弄所有的转换,我认为您需要做的是定义一个要反序列化的类。例如,使用 Jerkson:

import com.codahale.jerkson.Json._
import collection.immutable.{Map,List}

case class MyClass(test: Integer, myList: List[Map[String,String]])

object M {
  def main(args: Array[String]) {
    val t =parse[MyClass]("""{"test":"113123","myList":[{"test2":"321323"},{"test3":"11122"}]}""")
    println(t.myList(0)("test2"))
  }
}

(我把它写成一个完整的程序的原因是 Jerkson 不处理 REPL 中的反序列化案例类。)

我知道你可以使用 Jackson 反序列化为一个对象,但上次我查看它需要更多步骤。

这是在正确回答您的问题吗?

【讨论】:

    【解决方案4】:

    最终我按照 Leif 的建议编写了这段代码来解决问题

    private def mapToMap(map:java.util.Map[String,Any]) : Map[String,Any] = {
      val mb = new MapBuilder[String, Any, HashMap[String, String]](new HashMap[String, String])
      val iterator = map.keySet().iterator
      while (iterator.hasNext) {
        val key = iterator.next
        mb += (key -> checkValue(map.get(key)))
      }
    
      mb.result
    }
    
    private def arrayToList(array:java.util.ArrayList[Any]) : List[Any] = {
      val lb = new ListBuffer[Any]()
      val iterator = array.iterator
      while(iterator.hasNext) lb += checkValue(iterator.next)
      lb.result
    }
    
    private def checkValue(value:Any) : Any = value match {
      case l: java.util.ArrayList[Any] => arrayToList(l)
      case m: java.util.Map[String,Any] => mapToMap(m)
      case _ => value
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-20
      • 2021-07-04
      • 2016-10-14
      • 1970-01-01
      • 2021-08-29
      • 2015-04-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多