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
Shell Script in Android Gives [: Not Found
System Is Returning Error 127 When Called from C++ in Linux
Android 2.1 Programmatically Unmount Sdcard
How to Get Higher Precision of "Cpu%" Than That from Top Command
How to Set Up Android Sdk for Command Line Development on Linux
Permission Is Only Granted to System App
Error: No Resource Identifier Found for Attribute 'Adsize' in Package 'Com.Google.Example' Main.Xml
How to Set Margin of Imageview Using Code, Not Xml
Android: Creating Custom Preference
Install Shows Error in Console: Install Failed Conflicting Provider
Android Dialogfragment VS Dialog
How to Create a Table with Borders in Android
Differences Between Constraintlayout and Relativelayout
"Failed to Install the Following Android Sdk Packages as Some Licences Have Not Been Accepted" Error
Executing Google Apps Script Functions from Mobile App
Change the Color of a Checked Menu Item in a Navigation Drawer