Using Gson to Parse a JSON with Dynamic "Key" and "Value" in Android

Using GSON to parse a JSON with dynamic key and value in android

The most straightforward approach I can think of is to just treat the structure as a Map (of Map).

With Gson, this is relatively easy to do, as long as the Map structure is statically known, every branch from the root has the same depth, and everything is a String.

import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class GsonFoo
{
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
Type mapType = new TypeToken<Map<String,Map<String, String>>>() {}.getType();
Map<String,Map<String, String>> map = gson.fromJson(new FileReader("input.json"), mapType);
System.out.println(map);

// Get the count...
int count = Integer.parseInt(map.get("0").get("count"));

// Get each numbered entry...
for (int i = 1; i <= count; i++)
{
System.out.println("Entry " + i + ":");
Map<String, String> numberedEntry = map.get(String.valueOf(i));
for (String key : numberedEntry.keySet())
System.out.printf("key=%s, value=%s\n", key, numberedEntry.get(key));
}

// Get the routes...
Map<String, String> routes = map.get("routes");

// Get each route...
System.out.println("Routes:");
for (String key : routes.keySet())
System.out.printf("key=%s, value=%s\n", key, routes.get(key));
}
}

For more dynamic Map structure handling, I strongly suggest switching to use Jackson, instead of Gson, as Jackson will deserialize any JSON object of any arbitrary complexity into a Java Map, with just one simple line of code, and it will automatically retain the types of primitive values.

import java.io.File;
import java.util.Map;

import org.codehaus.jackson.map.ObjectMapper;

public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
Map map = mapper.readValue(new File("input.json"), Map.class);
System.out.println(map);
}
}

The same can be achieved with Gson, but it requires dozens of lines of code. (Plus, Gson has other shortcomings that make switching to Jackson well worth it.)

Parsing JSON response using GSON with dynamic Key

Model

public class Model {

private HashMap<String, String> data;

public Model() {
}
}

Convert json string to Hashmap using Gson & prepare data from hashmap

Gson gson = new Gson();
Type typeHashMap = new TypeToken<Map<String, String>>(){}.getType();
Map<String,String> map = gson.fromJson(YOUR_JSON, typeHashMap);

Set<Map.Entry<String, String>> entrySet = data.entrySet();

Iterator iterator = entrySet.iterator ();

for(int j = 0; j < entrySet.size(); j++) {
try {
Map.Entry entry = (Map.Entry) iterator.next();
String key = entry.getKey().toString();
String value = entry.getValue().toString();
//Add it to your list
}
catch(NoSuchElementException e) {
e.printStackTrace();
}
break;
}

How to parse a JSON with dynamic “key” in android by using GSON

How would you like your model class to look?

status and total would probably be int, so that only leaves info.

As an experiment, just add a field Object info and see how Gson would set it to an ArrayList<LinkedHashMap<String, String>> -- ugly and hard to access by key, but all the data is there. Given that information, the fastest way to model a class would be:

class Something {
int status;
List<Map<String, String> info;
int total;
}

If you have control over how that JSON is generated, I suggest changing the structure of info from an array of objects [{a:b},{c:d},{e:f}] to just an object {a:b,c:d,e:f}. With this, you could just map it to a Map<String, String> with all the benefits like access by key, keys() and values():

class Something {
int status;
Map<String, String> info;
int total;
}

If you want the latter model class without changing the JSON format, you'll have to write a TypeAdapter (or JsonDeserializer if you're only interested in parsing JSON, not generating it from your model class).

Here's a JsonDeserializer hat would map your original info JSON property to a plain Map<String, String>.

class ArrayOfObjectsToMapDeserializer
implements JsonDeserializer<Map<String, String>> {

public Map<String, String> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Map<String, String> result = new HashMap<String, String>();

JsonArray array = json.getAsJsonArray();
for (JsonElement element : array) {
JsonObject object = element.getAsJsonObject();
// This does not check if the objects only have one property, so JSON
// like [{a:b,c:d}{e:f}] will become a Map like {a:b,c:d,e:f} as well.
for (Entry<String, JsonElement> entry : object.entrySet()) {
String key = entry.getKey();
String value = entry.getValue().getAsString();
result.put(key, value);
}
}
return result;
}
}

You need to register this custom JsonDeserializer similar to this:

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
new TypeToken<Map<String, String>>() {}.getType(),
new ArrayOfObjectsToMapDeserializer());
Gson gson = builder.create();

Note that this registers the custom deserializer for any Map<String, String> regardless in what class it is encountered. If you don't want this, you'll need to create a custom TypeAdapterFactory as well and check the declaring class before returning and instance of the deserializer.

Parse JSON with dynamic key object inside dynamic key object with Gson

Your models cannot map your JSON just because Gson default configuration clearly gets them unmatched.

You can have two "default" ways:

static

... since you didn't really mention why your JSON is considered dynamic:

final class XYZ {

final ABC x = null;
final ABC y = null;
final ABC z = null;

}
final class ABC {

final OneTwo a = null;
final OneTwo b = null;

}
final class OneTwo {

@SerializedName("1")
final List<Integer> one = null;

@SerializedName("2")
final List<Integer> two = null;

}

Example:

try ( final Reader reader = getPackageResourceReader(Q43695739.class, "dynamic.json") ) {
final XYZ xyz = gson.fromJson(reader, XYZ.class);
System.out.println(xyz.x.b.two);
}

dynamic (by deserialization)

... assuming your keys are dynamic, but the structure remains the same:

private static final Type stringToStringToStringToIntegerListType = new TypeToken<Map<String, Map<String, Map<String, List<Integer>>>>>() {
}.getType();
try ( final Reader reader = getPackageResourceReader(Q43695739.class, "dynamic.json") ) {
final Map<String, Map<String, Map<String, List<Integer>>>> m = gson.fromJson(reader, stringToStringToStringToIntegerListType);
System.out.println(m.get("x").get("b").get("2"));
}

dynamic (by JSON trees)

Another true dynamic approach that may be helpful for some scenarios. Also note that JSONObject is not in the Gson realm: you probably might have imported this one from the org.json package. Gson uses camel-cased names like JsonElement, JsonObject, etc.

try ( final Reader reader = getPackageResourceReader(Q43695739.class, "dynamic.json") ) {
final JsonElement jsonElement = gson.fromJson(reader, JsonElement.class)
.getAsJsonObject()
.getAsJsonObject("x")
.getAsJsonObject("b")
.getAsJsonArray("2");
System.out.println(jsonElement);
}

The first and the second examples produce java.util.List instances

[1, 2, 3, 4]

The third example returns a JsonArray instance with a slightly different toString implementation:

[1,2,3,4]

Gson parser class for dynamic json key value - Android

Use this parser class

Meta meta = new Meta();

ArrayList<Activity> activity = new ArrayList<ActivityParser.Activity>();

ArrayList<String> notifications = new ArrayList<String>();

public class Meta
{
String code,dataPropertyName,currentTime,listedCount;
}

public class Activity
{
ArrayList<HashMap<String, CommentsItem>> comments = new ArrayList<HashMap<String,CommentsItem>>();

public class CommentsItem
{
String type,userPhoto,userId,postOwner,dateTime,sameOwner;
HashMap<String, String> userName = new HashMap<String,String>();
HashMap<String, PostDetails> postDetails = new HashMap<String,PostDetails>();
public class PostDetails
{
String postImage,postId,postType;
}

}

ArrayList<HashMap<String, FollowItem>> follow = new ArrayList<HashMap<String,FollowItem>>();

public class FollowItem
{
String type,followUserName,followByUserPhoto,followUserPhoto,dateTime;
HashMap<String, String> followByUserName = new HashMap<String,String>();
}
}

Parse Dynamic Key Json String using Retrofit

Your resultInside class is adding an extra object layer that does not exist in your JSON. Try moving the map to your Data class results field.

public class Data {
@SerializedName("results")
@Expose
private Map<String, Vitals> result;

//....
}

Retrofit parse JSON dynamic keys

You could make your model POJO contain a Map<String, Champion> to deserialize into, to deal with the dynamic keys.

Example:

public class ChampionData {
public Map<String, Champion> data;
public String type;
public String version;
}

public class Champion {
public int id;
public String title;
public String name;
public String key;
}

I'm not familiar with Retrofit besides that, but as someone in the comments said, the deserializing is done by Gson:

public ChampionData champions = new Gson().fromJson(json, ChampionData.class);

So to build on to the answer someone else posted, you can then do the following, assuming you've added the GsonConverterFactory:

public interface API {
@GET("path/to/endpoint")
Call<ChampionData> getChampionData();
}

Gson Parsing with dynamic json keys

Please try to use this, i have refined models based on json you provided, also the json you provided is not accurate due to wrong double quotes.

Here are my Pojo Classes

class YearWrapper {
List<YearObject> years;

public List<YearObject> getYears() {
return years;
}

public void setYears(List<YearObject> years) {
this.years = years;
}

@Override
public String toString() {
return "YearWrapper{" +
"years=" + years +
'}';
}
}

class YearObject {
int start;
int end;

public int getStart() {
return start;
}

public void setStart(int start) {
this.start = start;
}

public int getEnd() {
return end;
}

public void setEnd(int end) {
this.end = end;
}

@Override
public String toString() {
return "YearObject{" +
"start=" + start +
", end=" + end +
'}';
}
}

class KeyValueMapModel {
int position;
List<String> source;
@SerializedName("source_data")
Map<String, YearObject> sourceData;

public int getPosition() {
return position;
}

public void setPosition(int position) {
this.position = position;
}

public List<String> getSource() {
return source;
}

public void setSource(List<String> source) {
this.source = source;
}

public Map<String, YearObject> getSourceData() {
return sourceData;
}

public void setSourceData(Map<String, YearObject> sourceData) {
this.sourceData = sourceData;
}

@Override
public String toString() {
return "KeyValueMapModel{" +
"position=" + position +
", source=" + source +
", sourceData=" + sourceData +
'}';
}
}

Here is how i am invoking it:

Gson gson = new Gson();
KeyValueMapModel keyValueMapModel = gson.fromJson(yourJsonString, KeyValueMapModel.class);
System.out.println(keyValueMapModel);


Related Topics



Leave a reply



Submit