2012-01-09 79 views
2

我希望轉換json對象的列表,它們本身可能包含Json對象或Json對象列表,並希望結果爲適當的Scala Maps或列表,但是,我只能得到頂級對象轉換爲地圖或列表。 有沒有簡單的方法來做到這一點?傑克遜斯卡拉模塊,嵌套列表和地圖

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

你真的需要使用傑克遜嗎?在案例類中使用lift-json要簡單得多。 – 2012-01-09 18:30:17

+2

如果映射起作用,那麼在使用Jackson數據綁定時沒有任何複雜的事情:lift-json比上面顯示的一個線程更簡單嗎? – StaxMan 2012-01-09 19:00:18

+0

我已經使用電梯和傑克遜,並發現電梯處理15,000 + JSON對象時,如傑克遜處理這個罰款有內存問題 – 2012-01-09 19:54:00

回答

0

OK所以使用Scala的傑克遜模塊的新版本,我創建這個類

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) 
+0

我遇到了同樣的問題,但發現如果我只是沒有註冊UntypedObjectDeserializer,它的工作原理與我原本預期的一樣。 – jhodges 2014-03-06 15:47:11

0

如果如預期這不起作用,請申請訴傑克遜斯卡拉問題模塊在[https://github.com/FasterXML/jackson-module-scala/issues]。在「非類型化」模式(即非bean,使用列表,地圖等)中處理內容時,可能需要爲scala模塊添加特殊處理。根本的挑戰是Scala集合通常不會乾淨地映射到JDK集合。

+0

已記錄問題 – 2012-01-10 10:43:48

+0

好,讓我們希望它快速獲取地址! – StaxMan 2012-01-10 19:24:33

+0

這已被修復,創建新的答案,以顯示您需要使用的代碼 – 2013-01-04 14:34:40

0

我還沒有使用傑克遜Scala模塊,但Jerkson的行爲是一樣的,所以我會冒險猜測發生了什麼。當REPL是打印:

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

它告訴你,有含「myList中」和java.util.ArrayList中的元組。 ArrayList包含兩個java.util.LinkedHashMaps。 (我解釋這一點,因爲起初對我來說並不明顯,我不得不花費一些時間與REPL一起使用getClass和asInstanceOf來深入該結構。)

我相當確定沒有辦法獲得傑克遜Scala模塊或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反序列化情況下的類。)

我知道你可以反序列化到與傑克遜的對象,但上次我看了它需要更多的步驟。

這是正確的方向回答你的問題?

0

最後我也跟着雷夫的建議,寫了這個代碼來解決問題

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 
}