2010-04-20 9 views
11

Sto convertendo una lista di oggetti Foo in una stringa JSON. Ho bisogno di analizzare la stringa JSON in una lista di Foos. Comunque nel seguente esempio, l'analisi mi dà una lista di JSONObjects invece di Foos.Grails JSON array

Esempio

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 

Come posso analizzare in Foos, invece? Grazie in anticipo.

risposta

12

Ho dato un'occhiata ai documenti API per JSON e non sembra che esista un modo per analizzare una stringa JSON per un tipo specifico di oggetto.

Quindi devi solo scrivere il codice da solo per convertire ogni JSONObject in un Foo. Qualcosa del genere dovrebbe funzionare:

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

Una soluzione più generale potrebbe essere quella di aggiungere un nuovo metodo statico parse come il seguente al JSON metaclasse, che cerca di analizzare la stringa JSON a un elenco di oggetti di un particolare tipo:

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

È possibile provare il codice sopra nella console Groovy. Alcuni avvertimenti

  • Ho effettuato solo test molto limitato sul codice di cui sopra
  • Ci sono due classi di JSON nell'ultima Grails rilasciano, sto supponendo che si sta utilizzando quello che non è deprecato
+0

Sarebbe possibile iniettare gli attributi di ogni directoy di jsonObj nel campo foo.properties per ogni nuova istanza di Foo? – Armand

+0

@Ali G - No, penso che '.properties' sia scrivibile solo per gli oggetti del dominio Grails. Per gli oggetti Groovy regolari, penso che ".properties" sia di sola lettura. –

+0

Grazie Don. L'approccio generico è molto carino. – armandino

4

Se si sta facendo questo in un controllore Grails, e Foo è davvero un oggetto di dominio, non bisogna dimenticare che armati con la vostra carta JSON, si può anche fare:

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

o anche meglio Foo foo = new Foo (parsedList [0]) – gabe

4

ho preso questo codice e lo ha esteso per funzionare con strutture nidificate. Si basa su un attributo 'class' esistente nel JSON. Se ora c'è un modo migliore in Grails, fatemelo sapere.

 // 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 Sono sorpreso che non ci sia un modo migliore! – Armand

0

Come di Grails 2.5, questo è possibile:

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

uscita:

test 

non sono sicuro di quando è diventata un'opzione.