杰克逊斯卡拉模块,嵌套列表和地图

时间:2012-01-09 18:00:39

标签: json scala nested jackson

我希望转换json对象的列表,这些对象本身可能包含Json对象或Json对象列表,并且希望结果是Scala Maps或Lists,但是,我只能获得顶级对象转换为地图或列表。 有一种简单的方法可以做到这一点吗?

例如在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)))

4 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

我没有使用过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反序列化一个物体,但是上次我看了它又花了几步。

这是否正确回答您的问题?

答案 2 :(得分:0)

最终我遵循了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
}

答案 3 :(得分:0)

好的,所以使用更高版本的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)