web-dev-qa-db-fra.com

Comment décoder JSON avec un champ inconnu en utilisant Gson?

J'ai JSON similaire à ceci:

{
  "unknown_field": {
    "field1": "str",
    "field2": "str",
    "field3": "str",
    "field4": "str",
    "field5": "str"
  }, ......
}

J'ai créé des classes pour mapper ce json

public class MyModel implements Serializable {
  private int id;
  private HashMap<String, Model1> models;

  // getters and setter for id and models here
}

et la classe Model1 est une classe simple uniquement avec des champs String.

Mais ça ne marche pas.

Edit : le format JSON ressemble à ceci:

{
    "1145": {
        "cities_id": "1145",
        "city": "Nawanshahr",
        "city_path": "nawanshahr",
        "region_id": "53",
        "region_district_id": "381",
        "country_id": "0",
        "million": "0",
        "population": null,
        "region_name": "Punjab"
    },
    "1148": {
        "cities_id": "1148",
        "city": "Nimbahera",
        "city_path": "nimbahera",
        "region_id": "54",
        "region_district_id": "528",
        "country_id": "0",
        "million": "0",
        "population": null,
        "region_name": "Rajasthan"
    }, 
    ...
}
28
NazarK

(Après qu'OP ait commenté qu'en fait le JSON ressemble à ceci , j'ai complètement mis à jour la réponse.)

Solution pour Gson 2.0+

Je je viens d'apprendre qu'avec les nouvelles versions de Gson, c'est extrêmement simple:

GsonBuilder builder = new GsonBuilder();
Object o = builder.create().fromJson(json, Object.class);

L'objet créé est une carte (com.google.gson.internal.LinkedTreeMap), et si vous l'imprimez, il ressemble à ceci:

{1145={cities_id=1145, city=Nawanshahr, city_path=nawanshahr, region_id=53, region_district_id=381, country_id=0, million=0, population=null, region_name=Punjab}, 
 1148={cities_id=1148, city=Nimbahera, city_path=nimbahera, region_id=54, region_district_id=528, country_id=0, million=0, population=null, region_name=Rajasthan}
...

Solution utilisant un désérialiseur personnalisé

( [~ # ~] nb [~ # ~] : Il s'avère que vous n'êtes pas vraiment un désérialiseur personnalisé, sauf si vous êtes bloqué avec la version antérieure à 2.0 mais il est toujours utile de savoir comment faire désérialisation personnalisée (et sérialisation) dans Gson , et cela peut souvent être la meilleure approche, selon la façon dont vous souhaitez utiliser les données analysées. )

Il s'agit donc bien de noms de champs aléatoires/variables. (Bien sûr, ce format JSON n'est pas très bon; ce type de données devrait être à l'intérieur d'un tableau JSON, auquel cas il pourrait être très facilement lu dans une liste. Eh bien, nous pouvons toujours analyser cela.)

Tout d'abord, voici comment je modéliserais les données JSON dans les objets Java:

// info for individual city
public class City    {
    String citiesId;
    String city;
    String regionName;
    // and so on
}

// top-level object, containing info for lots of cities
public class CityList  {
    List<City> cities;

    public CityList(List<City> cities) {
        this.cities = cities;
    }
}

Ensuite, l'analyse. Une façon de gérer ce type de JSON consiste à créer un désérialiseur personnalisé pour l'objet de niveau supérieur (CityList).

Quelque chose comme ça:

public class CityListDeserializer implements JsonDeserializer<CityList> {

    @Override
    public CityList deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jsonObject = element.getAsJsonObject();
        List<City> cities = new ArrayList<City>();
        for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
            // For individual City objects, we can use default deserialisation:
            City city = context.deserialize(entry.getValue(), City.class); 
            cities.add(city);
        }
        return new CityList(cities);
    }

}

Un point clé à noter est l'appel à jsonObject.entrySet() qui retient tous les champs de niveau supérieur (avec des noms comme "1145", "1148", etc.). Cette réponse Stack Overflow m'a aidé à résoudre ce problème.

Complétez le code d'analyse ci-dessous. Notez que vous devez utiliser registerTypeAdapter() pour enregistrer le sérialiseur personnalisé.

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(CityList.class, new CityListDeserializer());
Gson gson = builder.setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
CityList list = gson.fromJson(json, CityList.class);

(Voici un complet, exemple exécutable que j'ai utilisé pour les tests. Outre Gson, il utilise la bibliothèque Guava.)

68
Jonik