How to decode JSON with unknown field using Gson?
(After OP commented that in fact the JSON looks like this, I completely updated the answer.)
Solution for Gson 2.0+
I just learned that with newer Gson versions this is extremely simple:
GsonBuilder builder = new GsonBuilder();
Object o = builder.create().fromJson(json, Object.class);
The created object is a Map (com.google.gson.internal.LinkedTreeMap), and if you print it, it looks like this:
{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 using a custom deserialiser
(NB: Turns out you don't really a custom deserialiser unless you're stuck with pre-2.0 versions of Gson. But still it is useful to know how to do custom deserialisation (and serialisation) in Gson, and it may often be the best approach, depending on how you want to use the parsed data.)
So we're indeed dealing with random / varying field names. (Of course, this JSON format is not very good; this kind of data should be inside a JSON array, in which case it could be very easily read into a List. Oh well, we can still parse this.)
First, this is how I would model the JSON data in Java objects:
// 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;
}
}
Then, the parsing. One way to deal with this kind of JSON is to create a custom deserialiser for the top level object (CityList).
Something like this:
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);
}
}
A key point to notice is the call to jsonObject.entrySet()
which retuns all the top-level fields (with names like "1145", "1148", etc). This Stack Overflow answer helped me solve this.
Complete parsing code below. Note that you need to use registerTypeAdapter()
to register the custom serialiser.
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);
(Here's a full, executable example that I used for testing. Besides Gson, it uses Guava library.)