2012-08-17 20 views
13

Come dovrei deserializzare seguendo JSON per saltare l'elemento radice e analizzare solo la parte interna di questo JSON. Mi piacerebbe evitare di creare ulteriori 3a classe Root, che includerebbe solo il campo MapWrapper.Salta l'elemento radice durante la deserializzazione di jSON

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

Così mi piacerebbe avere solo queste due classi:

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

class MyMapEntry { 
    String name; 
} 

risposta

10

è possibile utilizzare la libreria GSON per questo.

Il codice riportato di seguito risolverà il 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

Soluzione pulita. +1 –

+1

questo codice analizza JSON due volte. È possibile evitarlo? –

+0

sì, è possibile farlo scrivendo un JsonDeserializer personalizzato e registrandolo su GSON. – Byter

0

Si potrebbe deserializzare in un Map<String, MapWrapper>.

+0

Come stai deserializzazione i dati (o di pianificazione a)? (Qualche struttura particolare?) –

2

Questo è il codice ottimale per farlo in un solo passaggio.

MapWrapper classe

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 classe

public class MyMapEntry { 

    String name; 

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

Il Deserializer personalizzato

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

registrarlo con GSON

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

Ora deserialise con seguente codice

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

questo mi fa creare classi aggiuntive, che era il problema dall'inizio. Facendolo, è meglio creare la classe 'Wraper' con un campo di tipo' MapWrapper' chiamato root. –

+0

@Mathew Nessuna necessità di classe Wrapper in assoluto – Byter

+0

senza wrapper crea oggetto nullo, perché 'ctx.deserialize' chiamerebbe il metodo' MyDeserializer.deserialize' per creare l'oggetto 'MapWrapper' –

2

Si consideri il seguente JSON:

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

Il DTO per questo JSON senza l'elemento radice

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; 
    } 
} 

Converti da/per JSON utilizzando il i seguenti metodi (puoi tenerlo in DTO o in qualche altra classe di aiuto)

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

Buona soluzione per me – Riskhan

0

Ispirato da un'idea di Gustav Carlson ho deciso di espanderlo ad un campione di cemento. Ecco un test di junit che analizza l'analisi di questo JSON come 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

La mia risposta è in ritardo a questa festa.

Una volta analizzato il Json, il contenitore sarà sempre una sottoclasse JsonObject di JsonElement. Quindi, se vogliamo saltarlo, dobbiamo solo lanciarlo nella sua sottoclasse e afferrare il campo che tiene la nostra classe interiore.

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 e JSONObject sono diverse classi (utilizzare l'importazione com.google.Json).

È possibile generalizzare questa risposta in modo tale da non dover conoscere il nome della classe interna.Lo farebbe semplicemente ottenendo il campo di sola lettura dell'oggetto contenitore. Tuttavia, non vedo alcun modo per farlo se non avviare l'iteratore, non c'è alcun metodo getValue (atIndex) che possa vedere, e penso che l'avvio di un iteratore sia probabilmente meno efficiente rispetto alla semplice ricerca del campo per nome (ma potrebbe essere sbagliato).

Il metodo iteratore assomiglia:

JsonElement jsonQuery = ((JsonObject) jsonContainer).entrySet().iterator() 
          .next().getValue(); 
Problemi correlati