2012-05-15 28 views
19

Obtengo un json que tiene el campo "campo".
Si el "campo" tiene datos, entonces hay un OBJETO que tiene muchos (alrededor de 20) otros campos que también son objetos. Puedo deserializarlos sin ningún problema.
Pero si "campo" no tiene datos, es un ARRAY vacío (sé que es una locura, pero esa es la respuesta del servidor y no puedo cambiarlo). Algo como esto:Cómo invocar la deserialización predeterminada con gson

Cuando está vacío:

"extras":[ 

] 

tiene algunos datos:

"extras": { 
    "22":{ "name":"some name" }, 
    "59":{ "name":"some other name" }, 
    and so on... 
} 

Por lo tanto, cuando hay si no hay datos (matriz vacía), que, obviamente, obtener la excepción

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: 
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 4319 

He intentado utilizar JavaDeserializer personalizado:

public class ExtrasAdapter implements JsonDeserializer<Extras> { 
    @Override 
    public Extras deserialize(JsonElement json, Type typeOf, 
     JsonDeserializationContext context) throws JsonParseException { 
     try { 
      JsonObject jsonObject = json.getAsJsonObject(); 
      // deserialize normally 

      // the following does not work, as it makes recursive calls 
      // to the same function 
      //return context.deserialize(jsonObject, 
      //      new TypeToken<Object>(){}.getType()); 
     } catch (IllegalStateException e) { 
      return null; 
     } 
    } 
} 

leí el JSON de la siguiente manera

Gson gsonDecoder = new GsonBuilder().registerTypeAdapter(Extras.class, new ExtrasAdapter(); 
// httpResponse contains json with extras filed. 
Reader reader = new InputStreamReader(httpResponse.getEntity().getContent()); 
Extras response = gsonDecoder.fromJson(reader, Extras.class); 

no quiero deserializar los 20 campos manualmente (sé que esto es una opción), sólo quiero llamar context.defaultDeserialize (), o algo así.
Una vez más: no tengo problemas para deserializar json normal, crear objetos personalizados, registrar adaptadores de tipo personalizados, JavaDeserializers personalizados. Todo funciona ya. Solo necesito una solución para manejar datos, que pueden ser tanto ARRAY como OBJECT.
Gracias por cualquier ayuda.

======================


La respuesta del Joey funciona perfecto. Eso correcto lo que estaba buscando. Voy a publicar mi código aquí.

public class SafeTypeAdapterFactory implements TypeAdapterFactory { 
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { 
     final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); 
     return new TypeAdapter<T>() { 
      public void write(JsonWriter out, T value) throws IOException { 
       try { 
        delegate.write(out, value); 
       } catch (IOException e) { 
        delegate.write(out, null); 
       } 
      } 
      public T read(JsonReader in) throws IOException { 
       try { 
        return delegate.read(in); 
       } catch (IOException e) { 
        Log.w("Adapter Factory", "IOException. Value skipped"); 
        in.skipValue(); 
        return null; 
       } catch (IllegalStateException e) { 
        Log.w("Adapter Factory", "IllegalStateException. Value skipped"); 
        in.skipValue(); 
        return null; 
       } catch (JsonSyntaxException e) { 
        Log.w("Adapter Factory", "JsonSyntaxException. Value skipped"); 
        in.skipValue(); 
        return null; 
       } 
      } 
     }; 
    } 
} 
+0

pegue el código en el que está deserializando datos cuando el conjunto está disponible – waqaslam

+0

La matriz no puede estar disponible. Solo puede estar vacío. Si tuviera que deserializar la matriz, lo haría en el bloque catch. –

Respuesta

14

Trate de usar GSON> = 2.2.1 y buscar la clase TypeAdapterFactory.

Esto le dará la posibilidad de inspeccionar el objeto antes de deserializarlo y aplicar código personalizado al tiempo que se evitan las recurrencias.

Aquí hay un ejemplo de getDelegateAdapter que puede usar.

+1

+1 para getDelegateAdapter() Esta es la buena solución para implementar una funcionalidad común independientemente del tipo de objeto. Por ejemplo, llamar a una función post-deserialización en el objeto. – nidheeshdas

6
public class ExtrasAdapter implements JsonDeserializer<Extras> { 
@Override 
public Extras deserialize(JsonElement json, Type typeOf, 
       JsonDeserializationContext context) throws JsonParseException { 
    try { 
     JsonObject jsonObject = json.getAsJsonObject(); 
     return new Gson().fromJson(jsonObject , Extras.class); // default deserialization 

    } catch (IllegalStateException e) { 
     return null; 
    } 
} 
2

Para cualquiera que venga a fines, que no es necesario poner en práctica un TypeAdapter para resolver este problema, pero al hacerlo, es una solución perfectamente válida.

La respuesta a este problema es en realidad en la pregunta original:

public class ExtrasAdapter implements JsonDeserializer<Extras> { 

@Override 
public Extras deserialize(JsonElement json, Type typeOf, 
      JsonDeserializationContext context) throws JsonParseException { 
    try { 
     JsonObject jsonObject = json.getAsJsonObject(); 
     // deserialize normally 

     // the following does not work, as it makes recursive calls 
     // to the same function 
     //return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType()); 
    } catch (IllegalStateException e) { 
     return null; 
    } 
} 

La comentada

return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType()); 

es casi la solución. El problema es doble. Primero, jsonObject es el objeto exacto pasado a esta función originalmente.

JsonObject jsonObject = json.getAsJsonObject(); 

Así que pasarlo a context.deserialize() creará recursividad, y eventualmente OOM. La solución aquí es analizar los objetos dentro de jsonObject.

Esto nos lleva al segundo problema, que es que hay dos cosas que se mezclan aquí. "Extras" es un tipo de objeto, presumiblemente con una clase concreta que lo respalda (y posiblemente una matriz vacía). "Extra" es un mapa. Intentar analizar un "Extra" como un "Extras" no va a funcionar. A tal fin, sugeriría la siguiente definición de "Extras":

public class Extras { 
    Map<String, Map<String, String>> extras; 
    // you could also create a concrete class for "Extra" 
    //and have this be a Map<String, Extra> 
} 

En cuyo caso, el problema se vuelve trivial para resolver con context.deserialize.

Como indiqué anteriormente, un TypeAdatper es una solución perfectamente válida para este problema. Solo creo que es más de lo que necesitas.

Cuestiones relacionadas