2013-06-22 24 views
9

Eksperymentuję z biblioteką json4s (opartą na lift-json). Jedną z rzeczy, które chciałbym zrobić, to przetworzyć łańcuch JSON w AST, a następnie nim manipulować.Jak manipulować JSON AST w Scala

Na przykład, chciałbym wstawić pole (wstawić pole do AST, jeśli nie istnieje, lub zaktualizować jego wartość, jeśli to robi).

Nie znalazłem sposobu, jak to zrobić w dokumentacji. Eksperymentując z dostępnymi metodami, wymyśliłem następujące, które działa, ale czuję się niezdarny.

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

Nie lubię go z wielu powodów:

  1. Mając jawnie rzucić wyniki parse(json) do JObject
  2. Wynikiem funkcji upsertField jest JValue, które będę musiał przekształcenie jeśli Chcę manipulować obiektem dalej
  3. Funkcja po prostu wydaje się bardzo nieelegancka
  4. Nie działa dla pól, które nie znajdują się na najwyższym poziomie hierarchii.

Czy istnieje lepszy sposób na przekształcenie AST?

EDIT: jako obejście problemu, udało mi się przekształcić moje JSON do Scala regularnych zajęć i manipulować nimi z obiektywami (Using Lenses on Scala Regular Classes)

+0

co ma AST kandydowania? –

+1

@QuyTang AST oznacza "abstrakcyjne drzewo składniowe" – Eduardo

+0

Dzięki @Eduardo –

Odpowiedz

12

Istnieje funkcja scalania, która tworzy lub zastępuje pole. Możesz także zaktualizować pola, które nie znajdują się na poziomie głównym drzewa.

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

Kiedy wykonawczych jakiś bardzo specyficzny json diff użyciu dźwigu json I użyłem wielu funkcji rekurencyjnych, aby dostać się do jpath, gdzie potrzebuję zmodyfikować wartość, a zmodyfikowany json został skonstruowany, gdy rekursja "zwinięta". LiftJson jest w końcu niezmienny. Wspomniałeś soczewki jako inne podejście, które jest bardzo interesujące samo w sobie. Ale mój obecny faworyt biblioteka play-json, który działa jak czar, w sytuacji, kiedy trzeba zrobić JSON-to-json transformacji:

z Mandubian Blog: Cechy

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 

Yummy: Wszystkie transformatory są kombinatory, które można łączyć ze sobą, sprawdzanie poprawności, bezkształtne wsparcie, automatyczne buforowanie klas przypadków z domyślnymi przesłonięciami, samodzielna biblioteka.

1

Pozwól mi również daje wersję 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

Dziękuję. Wypróbuję w następnym projekcie! – Eduardo