2011-12-16 20 views
11

Ok, así que edité la pregunta porque no era lo suficientemente clara.GSON: deserialización de objetos personalizados

Editar 2: actualizó el archivo JSON.

Estoy usando GSON en una aplicación para Android, y necesito analizar archivos JSON, que provienen de un servidor, y son un poco demasiado complejos. No quiero que mi estructura de objetos sea demasiado pesada, por lo que me gustaría simplificar los contenidos: , por lo que la estructura de mi objeto no será la estructura del archivo JSON.

Por ejemplo, si en el JSON tengo esto:

{ 
    "object1":{ 
     "attribute1" : "test1", 
     "attribute40" : "test40", 
     "user":{ 
      "id":1, 
      "name":"foo" 
     } 
     ,"example":{ 
      "total":10, 
      "list":[ 
      { 
       "tag":"tag1", 
       "name":"object name 1", 
       "pos":1 
      }, 
      { 
       "tag":"tag10", 
       "name":"object name 10", 
       "pos":10 
      } 
     ] 
     } 
    } 
    "object2":{ 
     "attribute1":"test..." 
    } 
} 

no quiero tener en mi estructura de objeto actual, un objeto Example, que contiene un ArrayList y un "total" int . Pero me gustaría mantener solo una cadena simple con el valor "object name 1;object name 2;...".

Además, me gustaría almacenar solo el Id del usuario, no el Usuario completo, porque ya tengo el usuario completo almacenado en otro lugar, con otra llamada a la API del servidor.

Así que mi clase de la clase sería algo así como:

class Foo{ 
    int userId; 
    String example; //"object name 1;object name 2;..." 
    ... 
} 

así que supongo que podemos lograr esto con un deserializer costumbre, pero no encuentro la forma. Me gustaría si es posible minimizar la memoria, por lo que no creo que tener un ejemplo de objeto completo, y luego usarlo para construir mi String example es una forma correcta.

En el peor de los casos, si es demasiado complicado, me gustaría poder almacenar al menos solo la lista de elementos de etiqueta cuando analizo el objeto de ejemplo: entonces necesito un deserializador personalizado para deshacerme de la int total .

por lo que tendría:

class Foo{ 
    int userId; 
    ArrayList<Tag> example; 
    ... 
} 
+0

De acuerdo con sus requisitos especiales, GSON está sobrecargado. Simplemente pase su cadena JSON al constructor de su modelo de dominio y luego use la manipulación de cadena simple para dividir/extraer el campo requerido. – yorkw

+0

Bueno, seguro que podría ser una solución, pero tengo muchos archivos JSON con más de 30 campos cada vez, y la estructura puede evolucionar en el futuro. Por lo tanto, definitivamente preferiría usar una lib como GSON para minimizar el trabajo y el mantenimiento. Especialmente si ya lo uso en ciertos archivos JSON. – Chayy

+0

"la estructura puede evolucionar en el futuro", desde la perspectiva de OO, se recomienda encarecidamente modelar el objeto de dominio completo ahora, aunque no utilice todas sus propiedades. – yorkw

Respuesta

19

Adopté la respuesta para presentar la solución completa diseñada en el chat y para adaptarla a la cadena JSON modificada. El código asume que la cadena json contiene exactamente el JSON (actualizado) de la pregunta. El requisito es llenar la siguiente clase (setter y toString omitido):

class Object1 
{ 
    private String attribute1; 
    private String attribute40; 
    private int userId; 
    private String nameList; 
} 

soportes GSON (como las más otros REST-libs) tres modos:

  • GSON_DOM
    Reads todo el JSON a través JsonParser.parse() y crea un árbol DOM en la memoria (acceso al modelo de objetos). Por lo tanto, esta solución es buena para pequeños archivos JSON.
  • GSON_STREAM
    Lee solo trozos del JSON a través de JsonReader. El código es más complicado, pero es adecuado para grandes archivos JSON. A partir de Android 3.0 Honeycomb, el analizador de transmisión de GSON se incluye como android.util.JsonReader.
  • GSON_BIND
    Enlace de datos directamente a las clases mediante reflexión, minimiza el código de manera significativa. GSON permite el modo mixto, lo que significa combinar GSON_DOM y GSON_BIND o GSON_STREAM y GSON_BIND que esta respuesta debería mostrar.

para llenar la clase Object1 través GSON_DOM y GSON_BIND la puesta en práctica se parece a:

private static void deserializeViaObjectAccess(final String json) 
{ 
    Gson gson = new Gson(); 

    // Read the whole JSON into meomory via GSON_DOM 
    JsonParser parser = new JsonParser(); 
    JsonObject object1 = parser.parse(json).getAsJsonObject().getAsJsonObject("object1"); 

    // map the Object1 class via GSON_BIND 
    // (bind common attributes which exist in JSON and as properties in the class) 
    // mapper acts as factory 
    Object1 result = gson.fromJson(object1, Object1.class); 

    // manually read the attribute from the user object 
    int userId = object1.getAsJsonObject("user").getAsJsonPrimitive("id").getAsInt(); 
    result.setUserId(userId); 

    // manually read the attributes from the example object 
    String names = ""; 
    JsonArray list = object1.getAsJsonObject("example").getAsJsonArray("list"); 
    for (int i = 0; i < list.size(); ++i) 
    { 
     JsonObject entry = list.get(i).getAsJsonObject(); 
     String name = entry.getAsJsonPrimitive("name").getAsString(); 

     names = i == 0 ? name : names + "; " + name; 
    } 
    result.setNameList(names); 

    // Output the result 
    log.debug(result.toString()); 
} 

para llenar el Object1 clase a través de GSON_STREAM y GSON_BIND la puesta en práctica se parece a:

En el momento , esto solo es posible cuando un nodo se carga completamente mediante GSON_BIND o GSON_STREAM. Este ejemplo necesita que un nodo en sí mismo se divida. Esto es solo posible con la próxima versión 2.2. Le entregaré el código más adelante cuando GSON 2.2 esté disponible. *

+0

2.2.4 es la última disponible, por favor, háganos saber cómo usar GSON_BIND –

1

deserializar el ejemplo JSON en un ejemplo de objetos completo, utilice las propiedades del nombre del Ejemplo objeto de construir una cadena de las cosas que desea, olvidar el Ejemplo objeto.

Realmente no entiendo completamente la segunda pregunta, pero si tiene un objeto Test1 completo todos los campos/propiedades, entonces puede crear un objeto Test2 que tome los campos de Test1 que desee. Por ejemplo, su objeto Test2 puede aceptar Test1 como un parámetro en su constructor y tomar solo las propiedades que necesita ignorando el resto.

+0

Ok, mi pregunta era más: ¿cómo puedo hacer eso sin tener que almacenar todos los objetos en mi estructura? Por ejemplo, podría hacer el 'gson.fromJson()', que obtendrá todos los campos, excepto mi cadena concatenada, y luego hacer manualmente la concatenación. Lo mismo para mantener solo una identificación. Pero no quiero tener estos campos inútiles en la memoria, quiero hacer esto durante el pase de serialización. Para la segunda pregunta, no los dos objetos 'Test1' y' Test2' son independientes: el objeto 'Test2' podría crearse antes del objeto' Test1'. – Chayy

1

Mientras está transmitiendo en streaming en Jsons a través de http, simplemente puede Descartar el texto y solo almacenar sus objetos personalizados. En este caso, estará descartando continuamente la información innecesaria.

While stream not empty 

    Read the next block into a new string 

    Deserialize the string to the your object 

    Store the object in a myArrayList 

Nota: la lectura de todo el JSON y consumirlo, en su conjunto, es muy probable que sea necesario si desea que su aplicación sea robusto. A menos que quiera leer el JSON como un flujo de caracteres sin procesar (dudo, a menos que su JSON sea realmente, prohibitivamente grande, que este atajo sea necesario).

Sin embargo, leyendo el flujo de entrada sin imponer y requisitos de formación JSON bien, podría hacerse sin tener que escribir y estructuras de datos innecesarios a la memoria. Esto podría funcionar si solo desea un pequeño subconjunto de datos, es decirSolo quieres los nombres de las personas o las URL en el JSON. Pero se rompería si quieres estructuras de datos más complejas. Sin embargo:

// ejemplo, analizar las direcciones URL de líneas JSON sin almacenar toda la estructura de datos

While input stream is not empty 

    String x = nextLine 

    If x contains "http" 

     myArrayList.add(parseUrl(x) 

Consideraciones finales:

Bur, en última instancia, la solicitud Jsons RESTO no son como SQL-no se puede generucally y arbitrariamente ignore ciertos campos. Tal vez, si realmente desea Jsons de menor peso, el enfoque más natural es indicar o verificar si su proveedor de servicios RESt puede simplemente ampliar los tipos de parámetros de solicitudes RESt para acomodar su caso de uso.

+0

Muy bien por esta respuesta, es un enfoque que aún no probé. Pero como el Acceso al Modelo de Objeto que probé, con esta técnica necesita probar manualmente cada campo de nuevo los nombres de sus campos. Por supuesto, con esto puedo descartar la información innecesaria, pero también necesita mucho más trabajo y un mantenimiento más difícil. Incluso si su método es mejor en términos de rendimiento que el primero, no coincide totalmente con mis necesidades. Estaba buscando algo más automático, donde pudiera hacer mi trabajo específico en un objeto y dejar que los otros campos sean gestionados por el analizador. – Chayy

2

Una opción sería analizar la cadena JSON utilizando el analizador construido dentro de Gson como se detalla en here. Se podría hacer algo como esto:

com.google.gson.JsonParser parser = new JsonParser(); 
JsonObject object = parser.parse(data).getAsJsonObject(); 
JsonObject example = object.getAsJsonObject("example"); 
JsonArray list = example.getAsJsonArray("list"); 

JsonObject y JsonArray son parte de sí mismo Gson.

Después de usar esto puede usar funciones como getAsInt para analizar campos individuales y crear y devolver el objeto que desee.

Editar 1

Parece como que se puede utilizar gson.fromJson en una clase personalizada también y no sólo los tipos genéricos de Java como se da en este example. Así que tiene que analizar la cadena JSON mediante el análisis y la llamada de Json en uno de los objetos internos o matrices.

+0

Ok, ese es el primer enfoque que hice, con las clases de clásicos org.json. La desventaja de esta técnica es que tengo que hacer manualmente la coincidencia entre mis campos de objeto y los campos JSON. Es mucho trabajo y mantenimiento cuando tengo más de 30 a 40 campos. – Chayy

+0

Parece que también puedes usar de Json en una de las clases internas. Esto podría adaptarse a su necesidad. – Abhinav

0

Propongo utilizar la biblioteca Jackson. Se proporciona con licencia de Apache, por lo que puede usarlo para uso comercial gratuito. En tutorial, busque la capital "Streaming API Example". Es muy fácil y tú controlas el proceso de transmisión por completo. Entonces, puedes tomar lo que quieras e ignorar todas las otras cosas. La biblioteca de Jackson está dividida en algunos frascos. El contenedor que admite la API de transmisión es el más pequeño y no utiliza ningún otro. Es la respuesta, creo.

Jackson puede proporcionar aún más. En this article puede encontrar el método para leer el archivo JSON en el nivel superior, como elementos, pero estableciendo PREVIAMENTE qué objetos necesita y qué no. Por lo tanto, puede tener como resultado analizar inmediatamente solo los elementos que necesita.

+0

Parece que la biblioteca GSON también proporciona un modo de transmisión, incluso si parece menos eficiente que Jackson. Y de todos modos, como dije a los demás, no se ajusta a mis necesidades. – Chayy

+0

Quieres decir GSON, no JSON, ¿verdad? De todos modos, ¿desea que se reconstruya el objeto JSON, pero solo con los campos obligatorios? Tal vez, deberías volver a escribir tu pregunta. La pregunta no debe estar cerrada entre los comentarios. – Gangnus

+0

Puede hacer que se reconstruya el objeto JSON, pero solo con los campos obligatorios. De eso es de lo que estoy hablando en el segundo párrafo de mi respuesta. – Gangnus

Cuestiones relacionadas