2013-06-22 47 views
9

Tôi đang thử nghiệm với thư viện json4s (dựa trên thang máy-json). Một trong những điều tôi muốn làm là phân tích một chuỗi JSON thành một AST, và sau đó thao tác nó.Cách thao tác JSON AST trong Scala

Ví dụ, tôi muốn nâng cấp một trường (chèn trường vào AST nếu nó không tồn tại hoặc cập nhật giá trị của nó nếu nó có).

Tôi chưa thể tìm thấy cách thực hiện trong tài liệu. Thử nghiệm với các phương pháp có sẵn, tôi đã đưa ra những điều sau đây, hoạt động, nhưng cảm thấy vụng về.

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

Tôi không thích nó vì nhiều lý do:

  1. Phải đúc một cách rõ ràng các kết quả của parse(json)-JObject
  2. Kết quả của upsertField chức năng là một JValue, mà tôi sẽ phải viết lại nếu Tôi muốn thao tác đối tượng hơn nữa
  3. Chức năng upsertField chỉ cảm thấy rất không quan trọng
  4. Nó không hoạt động đối với các trường không ở cấp cao nhất của hệ thống phân cấp

Có cách nào tốt hơn để chuyển đổi AST không?

EDIT: như một cách giải quyết cho vấn đề, tôi đã được quản lý để chuyển đổi JSON của tôi đến các lớp học thường xuyên Scala, và thao tác chúng với ống kính (Using Lenses on Scala Regular Classes)

+0

gì AST đứng cho? –

+1

@QuyTang AST là viết tắt của "cây cú pháp trừu tượng" – Eduardo

+0

Cảm ơn @Eduardo –

Trả lời

12

Có chức năng hợp nhất tạo hoặc ghi đè trường. Bạn cũng có thể cập nhật các trường không ở cấp gốc của cây.

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

Khi tôi đang triển khai một số diff json rất cụ thể sử dụng thang máy json tôi đã sử dụng rất nhiều hàm đệ quy để đến đường dẫn mà tôi cần sửa đổi giá trị, và json đã sửa đổi được xây dựng khi đệ quy "bị sập". LiftJson là bất biến sau khi tất cả. Bạn đã đề cập đến các ống kính như một cách tiếp cận khác, điều này rất thú vị. Nhưng yêu thích hiện tại của tôi là thư viện play-json rằng đang làm việc như một nét duyên dáng trong một tình huống khi bạn cần làm json-to-json chuyển đổi:

từ Mandubian Blog: Các Đặc

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: tất cả các máy biến áp là combinators có thể được trộn lẫn với nhau, xác nhận, hỗ trợ shapeless, tự động marshaling của trường hợp các lớp học với overrides ngầm, thư viện độc lập.

1

Hãy để tôi cũng cung cấp cho bạn phiên bản 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

Cảm ơn bạn. Tôi sẽ thử nó trong dự án tiếp theo của tôi! – Eduardo