2012-08-17 25 views
13

¿Cómo debo deserializar después de JSON para omitir el elemento raíz y analizar solo la parte interna de este JSON. Me gustaría evitar crear 3ª clase adicional, Root, que incluiría solo el campo MapWrapper.Omitir elemento raíz mientras se deserializa json

{ 
    "root": { 
     "language": "en", 
     "map": { 
      "k1": { 
       "name": "n1", 
      }, 
      "k2": { 
       "name": "n2", 
      } 
     } 
    } 
} 

Así que me gustaría tener sólo estas dos clases:

class MapWrapper { 
    private String language; 
    private Map<String, MyMapEntry> map; 
} 

class MyMapEntry { 
    String name; 
} 

Respuesta

10

puede usar GSON Biblioteca para esto.

El siguiente código resolverá su problema.

public class ConvertJsonToObject { 

    private static Gson gson = new GsonBuilder().create(); 

    public static final <T> T getFromJSON(String json, Class<T> clazz) { 
     return gson.fromJson(json, clazz); 
    } 

    public static final <T> String toJSON(T clazz) { 
     return gson.toJson(clazz); 
    } 
} 

String json; // your jsonString 
Map<String,Object> r = ConvertJsonToObject.getFromJSON(json,Map.class); 
String innerJson = ConvertJsonToObject.toJson(r.get("root")); 
MapWrapper _r = ConvertJsonToObject.getFromJSON(innerJson,MapWrapper.class); 
+0

Solución limpia. +1 –

+1

este código analiza json dos veces. ¿Es posible evitarlo? –

+0

sí, es posible hacerlo escribiendo un JsonDeserializer personalizado y registrándolo en GSON. – Byter

0

Usted puede deserializar en un Map<String, MapWrapper>.

+0

¿Cómo estás deserializar los datos (o la planificación a)? (Cualquier marco particular?) –

2

Este es el código óptimo para hacerlo en una sola pasada.

MapWrapper clase

public class MapWrapper { 
    private String language; 
    private Map<String, MyMapEntry> map; 

    public MapWrapper(String language, Map<String, MyMapEntry> map) { 
     this.language = language; 
     this.map = map; 
    } 
} 

MyMapEntry clase

public class MyMapEntry { 

    String name; 

    public MyMapEntry(String name) { 
     this.name = name; 
    } 
} 

deserializador personalizada

public class MyDeserialiser implements JsonDeserializer<MapWrapper> 
{ 

    @Override 
    public MapWrapper deserialize(JsonElement json, Type typeOfT, 
     JsonDeserializationContext ctx) throws JsonParseException { 

     JsonObject _global = json.getAsJsonObject(); 
     _global = _global.get("root").getAsJsonObject(); 

     JsonPrimitive lang = (JsonPrimitive) _global.get("language"); 
     JsonElement map = _global.get("map"); 
     Map<String, MyMapEntry> inMap = new LinkedHashMap<String, MyMapEntry>(); 
     for (Entry<String, JsonElement> entry : map.getAsJsonObject() 
       .entrySet()) { 
      MyMapEntry _m = new MyMapEntry(entry.getValue().toString()); 
      inMap.put(entry.getKey(), _m); 
     } 
     return new MapWrapper(lang.getAsString(), inMap); 
    } 
} 

verlo con GSON

new GsonBuilder().registerTypeAdapter(MapWrapper.class,new MyDeserialiser()).create() 

Ahora deserialise con siguiente código

String json; // your jsonString 
MapWrapper result = ConvertJsonToObject.getFromJSON(json,MapWrapper.class); 
+0

esto me hace crear una clase adicional, que era el problema desde el principio. Al hacerlo, es mejor crear la clase 'Wraper' con un campo de tipo' MapWrapper' llamado root. –

+0

@Mathew No hay necesidad de la clase Wrapper en realidad – Byter

+0

sin envoltura crea objeto nulo, porque 'ctx.deserialize' llamaría al método' MyDeserializer.deserialize' para crear el objeto 'MapWrapper' –

2

Considere el siguiente JSON:

{"authorization":{"username":"userabc", "password":"passabc"}} 

El DTO para este JSON sin el elemento raíz

public class Authorization { 
    private String username; 
    private String password; 

    public String getUsername() { 
     return username; 
    } 

    public void setUsername(String username) { 
     this.username = username; 
    } 

    public String getPassword() { 
     return password; 
    } 

    public void setPassword(String password) { 
     this.password = password; 
    } 

    // Add a container for the root element 
    public static class Container { 
     public Authorization authorization; 
    } 
} 

convertir desde/hacia JSON utilizando el siguientes métodos (puede mantener esto dentro de DTO o alguna otra clase de ayuda)

public String toJson(Authorization authorization) { 
    Gson gson = new Gson(); 
    Authorization.Container container = new Authorization.Container(); 
    container.authorization = authorization; 
    return gson.toJson(container); 
} 

public Authorization fromJson(String json) { 
    Gson gson = new Gson(); 
    Authorization.Container container = gson.fromJson(json, Authorization.Container.class); 
    return container.authorization; 
} 
+0

Buena solución wrt me – Riskhan

0

Inspirado en la idea de Gustav Carlson, decidí expandirlo a una muestra de hormigón. Aquí hay una prueba junit que prueba el análisis de este JSON como Map.

public static class MapWrapper { 
    private String language; 
    private Map<String, MyMapEntry> map; 
} 

public static class MyMapEntry { 
    String name; 
} 

@Test 
public void testParsing() { 
    String json = "{\n" + 
      " \"root\": {\n" + 
      "  \"language\": \"en\",\n" + 
      "  \"map\": {\n" + 
      "   \"k1\": {\n" + 
      "    \"name\": \"n1\"\n" + 
      "   },\n" + 
      "   \"k2\": {\n" + 
      "    \"name\": \"n2\"\n" + 
      "   }\n" + 
      "  }\n" + 
      " }\n" + 
      "}"; 
    Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); 
    Type type = new TypeToken<Map<String, MapWrapper>>(){}.getType(); 
    Map<String, MapWrapper> parsed = gson.fromJson(json, type); 
    MapWrapper mapWrapper = parsed.get("root"); 
    Assert.assertEquals("en", mapWrapper.language); 
    Assert.assertEquals("n2", mapWrapper.map.get("k2").name); 
} 
2

Mi respuesta es tardía para esta fiesta.

Una vez que analizamos el Json, el contenedor siempre será una subclase JsonObject de JsonElement. Por lo tanto, si queremos saltearlo, solo tenemos que lanzarlo a su subclase y tomar el campo que contiene nuestra clase interna.

String response = ....; 

    Gson gson = new Gson(); 

    JsonParser p = new JsonParser(); 
    JsonElement jsonContainer = p.parse(response); 
    JsonElement jsonQuery = ((JsonObject) jsonContainer).get("query"); 

    MyQuery query = gson.fromJson(jsonQuery, MyQuery.class); 

Nota: JSONObject y JSONObject son diferentes clases (utilizar la importación com.google.Json).

Puede generalizar esta respuesta de forma tal que no necesite saber el nombre de la clase interna.Haría esto simplemente obteniendo el campo de uno y solo del objeto contenedor. Sin embargo, no veo otra manera de hacerlo que iniciar el iterador, no hay un método getValue (atIndex) que pueda ver, y creo que iniciar un iterador es probablemente menos eficiente que simplemente buscar el campo por nombre (pero podría ser incorrecto).

El método iterador se parece a:

JsonElement jsonQuery = ((JsonObject) jsonContainer).entrySet().iterator() 
          .next().getValue(); 
Cuestiones relacionadas