2010-04-20 18 views
11

Estoy convirtiendo una lista de objetos Foo en una cadena JSON. Necesito analizar la cadena JSON nuevamente en una lista de Foos. Sin embargo, en el siguiente ejemplo, el análisis me da una lista de JSONObjects en lugar de Foos.Grails JSON array

Ejemplo

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 

¿Cómo puedo analizar en Foos en su lugar? Gracias de antemano.

Respuesta

12

He echado un vistazo a los documentos API para JSON y no parece haber ninguna forma de analizar una cadena JSON para un tipo específico de objeto.

Así que solo tendrá que escribir el código usted mismo para convertir cada JSONObject en Foo. Algo como esto debería funcionar:

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 solución más general sería añadir un nuevo método estático parse como la siguiente a la JSON MetaClass, que trata de analizar la cadena JSON a una lista de objetos de una determinada 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'] } 

Puede probar el código anterior en la consola groovy. Unas pocas advertencias

  • sólo han llevado a cabo pruebas muy limitado en el código anterior
  • Hay dos clases de JSON en la última Griales liberan, estoy asumiendo que usted está usando el que no está en desuso
+0

¿Sería posible inyectar los atributos de cada directorio jsonObj en el campo foo.properties para cada nueva instancia de Foo? – Armand

+0

@Ali G - No, creo que '.properties' solo se puede escribir en objetos de dominio de Grails. Para los objetos normales de Groovy, creo que '.properties' es de solo lectura. –

+0

Gracias Don. El enfoque genérico es muy agradable. – armandino

4

Si usted está haciendo esto en un controlador de Grails, y foo es de hecho un objeto de dominio, no se olvide de que armado con su mapa JSON, también se puede hacer:

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 incluso mejor Foo foo = new Foo (lista_de_análisis [0]) – gabe

4

que he tomado este código y lo extendió para trabajar con estructuras anidadas. Se basa en un atributo 'clase' existente en el JSON. Si hay una mejor manera por ahora en Grails, házmelo saber.

 // 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 ¡Me sorprende que no haya una manera mejor! – Armand

0

A partir de Grails 2.5, esto es posible:

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

Salida:

test 

estoy seguro de cuando se convirtió en una opción.

Cuestiones relacionadas