2013-06-22 38 views
9

我正在試驗json4s庫(基於lift-json)。我想要做的事情之一是將JSON字符串解析爲AST,然後對其進行處理。如何操縱Scala中的JSON AST

例如,我想插入一個字段(如果該字段不存在,則將該字段插入到AST中,或者如果它存在,則更新其值)。

我一直無法找到如何在文檔中做到這一點。嘗試使用可用的方法,我提出了以下方法,這種方法可行,但感覺笨拙。

import org.json4s._ 
import org.json4s.JsonDSL._ 
import org.json4s.native.JsonMethods._ 

object TestJson { 
    implicit val formats = DefaultFormats 

    def main(args: Array[String]): Unit = { 
    val json = """{"foo":1, "bar":{"foo":2}}""" 
    val ast = parse(json).asInstanceOf[JObject] 

    println(upsertField(ast, ("foo" -> "3"))) 
    println(upsertField(ast, ("foobar" -> "3"))) 
    } 

    def upsertField(src:JObject, fld:JField): JValue = { 
    if(src \ fld._1 == JNothing){ 
     src ~ fld 
    } 
    else{ 
     src.replace(List(fld._1), fld._2) 
    } 
    } 
} 

我不喜歡它的原因有很多:

  1. 了不得不parse(json)結果顯式轉換爲JObject
  2. upsertField函數的結果是一個JValue,我將不得不重鑄如果我想進一步操作對象
  3. upsertField功能只是覺得很不起眼
  4. 它不適用於不在層次結構頂層的字段

是否有更好的方法來轉換AST?

編輯:作爲一種解決方法的問題,我已成功地我的JSON轉換爲斯卡拉普通班,並與鏡頭操縱它們(Using Lenses on Scala Regular Classes

+0

什麼AST立場? –

+1

@QuyTang AST代表「抽象語法樹」 – Eduardo

+0

謝謝@Eduardo –

回答

12

有合併功能可以創建或覆蓋一個字段。您還可以更新不在樹根級別的字段。

import org.json4s._ 
import org.json4s.JsonDSL._ 
import org.json4s.jackson.JsonMethods._ 

object mergeJson extends App { 

    val json = 
    """ 
     |{ 
     | "foo":1, 
     | "bar": { 
     | "foo": 2 
     | } 
     |} 
     |""".stripMargin 

    val ast = parse(json) 

    val updated = ast merge (("foo", 3) ~ ("bar", ("fnord", 5))) 

    println(pretty(updated)) 

    // { 
    // "foo" : 3, 
    // "bar" : { 
    //  "foo" : 2, 
    //  "fnord" : 5 
    // } 
    // } 

} 
0

當我使用電梯JSON我實施一些非常具體的JSON差異使用了很多遞歸函數來到達我需要修改值的jpath,而修改後的json是在遞歸「摺疊」時構建的。 LiftJson畢竟是不可變的。你提到鏡頭是另一種方法,這本身非常有趣。但我目前最喜歡的是工作像一個情況的魅力,當你需要做的JSON到JSON的轉換播放JSON庫:從Mandubian Blog

val gizmo2gremlin = (
    (__ \ 'name).json.put(JsString("gremlin")) and 
    (__ \ 'description).json.pickBranch(
     (__ \ 'size).json.update(of[JsNumber].map{ case JsNumber(size) => JsNumber(size * 3) }) and 
     (__ \ 'features).json.put(Json.arr("skinny", "ugly", "evil")) and 
     (__ \ 'danger).json.put(JsString("always")) 
     reduce 
) and 
    (__ \ 'hates).json.copyFrom((__ \ 'loves).json.pick) 
) reduce 

美味的特點:所有的變壓器可以混合在一起的組合器,驗證,無定形支持,具有隱式替代的案例類的自動編組,獨立庫。

1

還讓我給你SON of JSON版本:

import nl.typeset.sonofjson._ 

val json = parse("""{ "foo" : 1, "bar" : { "foo" : 2 } }""") 

// or, perhaps a little easier 
val json = obj(foo = 1, bar = obj(foo = 2)) 

json.foo = "3" 
json.foobar = "3" 
+0

謝謝。我會在下一個項目中嘗試一下! – Eduardo