2010-04-20 25 views
11

Tôi đang chuyển đổi danh sách các đối tượng Foo thành chuỗi JSON. Tôi cần phải phân tích chuỗi JSON trở lại thành danh sách các Foos. Tuy nhiên trong ví dụ sau, phân tích cú pháp cung cấp cho tôi một danh sách các JSONObject thay vì Foos.Mảng JSON Grails

Ví dụ

List list = [new Foo("first"), new Foo("second")] 
def jsonString = (list as JSON).toString() 

List parsedList = JSON.parse(jsonString) as List 
println parsedList[0].getClass() // org.codehaus.groovy.grails.web.json.JSONObject 

Làm thế nào tôi có thể phân tích nó thành Foos để thay thế? Cảm ơn trước.

Trả lời

12

Tôi đã xem tài liệu API cho JSON và dường như không có cách nào để phân tích cú pháp thành chuỗi JSON thành một loại đối tượng cụ thể.

Vì vậy, bạn sẽ chỉ phải tự viết mã để chuyển đổi từng số JSONObject thành Foo. Một cái gì đó như thế này nên làm việc:

import grails.converters.JSON 
import org.codehaus.groovy.grails.web.json.* 

class Foo { 
    def name 

    Foo(name) { 
    this.name = name 
    } 

    String toString() { 
    name 
    } 
} 


List list = [new Foo("first"), new Foo("second")] 
def jsonString = (list as JSON).toString() 

List parsedList = JSON.parse(jsonString) 

// Convert from a list of JSONObject to a list of Foo 
def foos = parsedList.collect {JSONObject jsonObject -> 
    new Foo(name: jsonObject.get("name")) 
} 

Một giải pháp tổng quát hơn sẽ có thêm một parse phương pháp tĩnh mới như sau vào JSON metaclass, mà cố gắng để phân tích chuỗi JSON để một danh sách các đối tượng của một đặc biệt loại:

import grails.converters.JSON 
import org.codehaus.groovy.grails.web.json.* 

class Foo { 
    def name 

    Foo(name) { 
    this.name = name 
    } 

    String toString() { 
    name 
    } 
} 

List list = [new Foo("first"), new Foo("second")] 
def jsonString = (list as JSON).toString() 


List parsedList = JSON.parse(jsonString) 

// Define the new method 
JSON.metaClass.static.parse = {String json, Class clazz -> 

    List jsonObjs = JSON.parse(json) 

    jsonObjs.collect {JSONObject jsonObj -> 

     // If the user hasn't provided a targetClass read the 'class' proprerty in the JSON to figure out which type to convert to 
     def targetClass = clazz ?: jsonObj.get('class') as Class 
     def targetInstance = targetClass.newInstance()   

     // Set the properties of targetInstance 
     jsonObj.entrySet().each {entry -> 

      if (entry.key != "class") { 
       targetInstance."$entry.key" = entry.value 
      } 
     } 
     targetInstance 
    } 

} 

// Try the new parse method 
List<Foo> foos = JSON.parse(jsonString, Foo) 

// Confirm it worked 
assert foos.every {Foo foo -> foo.class == Foo && foo.name in ['first', 'second'] } 

Bạn có thể thử mã ở trên trong bảng điều khiển groovy. Một vài cảnh báo

  • Tôi đã chỉ thực hiện thử nghiệm rất hạn chế trên mã trên
  • Có hai lớp JSON trong mới nhất Grails phát hành, tôi giả sử bạn đang sử dụng một trong đó không bị phản đối
+0

Có thể tiêm các thuộc tính của mỗi director jsonObj vào trường foo.properties cho mỗi cá thể mới của Foo không? – Armand

+0

@Ali G - Không, tôi nghĩ '.properties' chỉ có thể ghi cho đối tượng miền Grails. Đối với các đối tượng Groovy thông thường, tôi nghĩ '.properties' là chỉ đọc. –

+0

Cảm ơn Don. Cách tiếp cận chung là rất tốt đẹp. – armandino

4

Nếu bạn đang làm điều này trong một bộ điều khiển Grails, và Foo thực sự là một đối tượng tên miền, đừng quên rằng vũ trang với bản đồ JSON của bạn, bạn cũng có thể làm:

List list = [new Foo("first"), new Foo("second")] 
def jsonString = (list as JSON).toString() 

List parsedList = JSON.parse(jsonString) as List 
Foo foo = new Foo() 
bindData(foo, parsedList[0]); 
+0

hoặc thậm chí tốt hơn Foo foo = new Foo (parsedList [0]) – gabe

4

tôi đã lấy mã này và mở rộng nó để làm việc với các cấu trúc lồng nhau. Nó dựa vào thuộc tính 'class' tồn tại trong JSON. Nếu có một cách tốt hơn bây giờ trong Grails xin vui lòng cho tôi biết.

 // The default JSON parser just creates generic JSON objects. If there are nested 

    // JSON arrays they are not converted to theirs types but are left as JSON objects 
    // This converts nested JSON structures into their types. 
    // IT RELIES ON A PROPERTY 'class' that must exist in the JSON tags 
    JSON.metaClass.static.parseJSONToTyped = {def jsonObjects -> 

     def typedObjects = jsonObjects.collect {JSONObject jsonObject -> 
      if(!jsonObject.has("class")){ 
       throw new Exception("JSON parsing failed due to the 'class' attribute missing: " + jsonObject) 
      } 

      def targetClass = grailsApplication.classLoader.loadClass(jsonObject.get("class")) 
      def targetInstance = targetClass.newInstance() 

      // Set the properties of targetInstance 
      jsonObject.entrySet().each {entry -> 
       // If the entry is an array then recurse 
       if(entry.value instanceof org.codehaus.groovy.grails.web.json.JSONArray){ 
        def typedSubObjects = parseJSONToTyped(entry.value) 
        targetInstance."$entry.key" = typedSubObjects 
       } 
       else if (entry.key != "class") { 
        targetInstance."$entry.key" = entry.value 
       } 
      } 

      targetInstance 
     } 

     return typedObjects 
    } 
+0

+1 Tôi ngạc nhiên không có cách nào tốt hơn! – Armand

0

Tính đến Grails 2.5, điều này có thể:

Period test = new Period() 
test.periodText = 'test' 
String j = test as JSON 
def p = JSON.parse(j) 
test = p.asType(Period) 
println(test.periodText) 

Output:

test 

Tôi không chắc chắn khi nào nó trở thành một lựa chọn.