2010-04-20 14 views
11

Konwertuję listę obiektów Foo na ciąg JSON. Muszę sparsować ciąg JSON z powrotem na listę Foos. Jednak w poniższym przykładzie, parsowanie daje mi listę JSONObjects zamiast Foos.Macierz JSON Grails

Przykład

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 

Jak mogę analizować je pod Foo zamiast? Z góry dzięki.

Odpowiedz

12

Zerknąłem na dokumenty API dla JSON i nie wydaje się, żeby można było przetworzyć łańcuch JSON na konkretny typ obiektu.

Musisz napisać kod, aby przekonwertować każdy kod JSONObject na Foo. Coś jak to powinno działać:

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

Bardziej ogólnie rozwiązaniem byłoby, aby dodać nową statyczną parse metody, takie jak następujące w JSON metaklasa, że ​​stara się analizować łańcuch JSON do listy obiektów danego wpisz:

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'] } 

Możesz wypróbować powyższy kod w konsoli groovy. Kilka ostrzeżenia

  • Mam wykonywane jedynie bardzo ograniczone badania na kodzie powyżej
  • Istnieją dwie klasy JSON w najnowszym Grails wydaniu, jestem przy założeniu, że używasz jednego, że nie jest nieaktualna
+0

Czy byłoby możliwe wprowadzenie atrybutów każdego directoy jsonObj w polu foo.properties dla każdej nowej instancji Foo? – Armand

+0

@Ali G - Nie, myślę, że '.properties' jest zapisywalny tylko dla obiektów domeny Grails. W przypadku zwykłych obiektów Groovy, myślę, że '.properties' jest tylko do odczytu. –

+0

Dzięki Don. Ogólne podejście jest bardzo miłe. – armandino

4

Jeśli robisz to w kontrolerze Grails i Foo jest rzeczywiście obiekt domeny, nie należy zapominać, że uzbrojony z JSON mapie można również zrobić:

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

lub jeszcze lepiej Foo foo = nowy Foo (parseList [0]) – gabe

4

Wziąłem ten kod i rozszerzy go, aby działał z strukturami zagnieżdżonymi. Opiera się na atrybucie "klasa" istniejącym w JSON. Jeśli w Grasils jest teraz lepszy sposób, daj mi znać.

 // 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 Jestem zaskoczony, że nie ma lepszego sposobu! – Armand

0

Od Grails 2.5, jest to możliwe:

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

wyjściowa:

test 

Jestem pewny, kiedy stało się to możliwe.