Parsing JSON with GSON, object sometimes contains list sometimes contains object
With Gson, the only way I know how to handle situations like this is with a custom Deserializer. For example:
// outputs:
// [Container: obj=[ChildContainer: children=[[Child: id=1], [Child: id=2]]]]
// [Container: obj=[ChildContainer: children=[[Child: id=1]]]]
public class Foo
{
static String json1 = "{\"obj\":{\"children\":[{\"id\":\"1\"},{\"id\":\"2\"}]}}";
static String json2 = "{\"obj\":{\"children\":{\"id\":\"1\"}}}";
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
gsonBuilder.registerTypeAdapter(Child[].class, new ChildrenDeserializer());
Gson gson = gsonBuilder.create();
Container container1 = gson.fromJson(json1, Container.class);
System.out.println(container1);
Container container2 = gson.fromJson(json2, Container.class);
System.out.println(container2);
}
}
class Container
{
ChildContainer obj;
@Override
public String toString()
{
return String.format("[Container: obj=%1$s]", obj);
}
}
class ChildContainer
{
Child[] children;
@Override
public String toString()
{
return String.format("[ChildContainer: children=%1$s]", Arrays.toString(children));
}
}
class Child
{
String id;
@Override
public String toString()
{
return String.format("[Child: id=%1$s]", id);
}
}
class ChildrenDeserializer implements JsonDeserializer<Child[]>
{
@Override
public Child[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
if (json instanceof JsonArray)
{
return new Gson().fromJson(json, Child[].class);
}
Child child = context.deserialize(json, Child.class);
return new Child[] { child };
}
}
Parse Json with Gson and problems with list
A quick way of doing it is as follows
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
public class Clazz {
public static void main(String[] args) throws Exception {
String singularJson = "{ Token: { TokenId : '123' } }";
String multipleJson = "{ Token: [{ TokenId : '123' }, { TokenId : '124' }] }";
JsonElement jsonElementToken = new JsonParser().parse(multipleJson);
JsonElement jsonCollectionOrSingular = jsonElementToken.getAsJsonObject().get("Token");
if (jsonCollectionOrSingular.isJsonArray()) {
System.out.println("It is an collection and not a object");
JsonArray jsonArray = jsonCollectionOrSingular.getAsJsonArray();
System.out.println(jsonArray.get(0).getAsJsonObject().get("TokenId"));
} else {
System.out.println("It is an object and not a collection");
JsonObject jsonObject = jsonCollectionOrSingular.getAsJsonObject();
System.out.println(jsonObject.get("TokenId"));
}
}
Gson: handling an optional List
I don't know what is the structure of a class that you are using to deserialize this JSON, but I'd like to sugest that there is a class like this
public class Rule {
private String id;
private String field;
private String type;
private String input;
private String operator;
private List<String> value;
// constructors, getters and setters
}
As you can see the value
property is defined as a list of java.lang.String
objects. This approach will help us to handle both cases: when value
is an array and when it's a simple string value.
What can we do with the Gson? We can create custom deserializer like this
public class CustomSerializer implements JsonDeserializer<Rule> {
public Rule deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject obj = (JsonObject) jsonElement;
JsonElement value = obj.get("value");
List<String> values = new ArrayList();
if (value.isJsonArray()) {
for (JsonElement jsonElement1 : value.getAsJsonArray()) {
String str = jsonElement1.getAsString();
values.add(str);
}
} else {
values.add(value.getAsString());
}
// deserialize other properties
Rule rule = new Rule();
rule.setValue(values);
// set other properties
return rule;
}
}
After that you need to register deserializer with
Gson gson = new GsonBuilder()
.registerTypeAdapter(Rule.class, new CustomSerializer ())
.create();
and you can deserialize JSON object like
{
"id": "date",
"field": "date",
"type": "date",
"input": "text",
"operator": "equal",
"value": "01.01.2016"
}
that is a part of your whole data structure.
Deserialization of sometimes string and sometimes object with Gson
One solution to your problem is to write a TypeAdapter
for your class, however if you have only cases like that in your example, you can achieve the same result letting Gson do the job for you using the most generic class you can for deserialization.
What I mean is shown in the below code.
package stackoverflow.questions.q19478087;
import com.google.gson.Gson;
public class Q19478087 {
public class Test {
public int id;
public Object blob;
@Override
public String toString() {
return "Test [id=" + id + ", blob=" + blob + "]";
}
}
public static void main(String[] str){
String json1 = "{\"id\": 1, \"blob\": \"example text\"}";
String json2 = "{\"id\": 2, \"blob\": {\"to\": 1234, \"from\": 4321, \"name\": \"My_Name\"}}";
Gson g = new Gson();
Test test1 = g.fromJson(json1, Test.class);
System.out.println("Test 1: "+ test1);
Test test2 = g.fromJson(json2, Test.class);
System.out.println("Test 2: "+ test2);
}
}
and this is my execution:
Test 1: Test [id=1, blob=example text]
Test 2: Test [id=2, blob={to=1234.0, from=4321.0, name=My_Name}]
In second case, blob will be deserialized as a LinkedTreeMap, so you can access its elements using ((Map) test2.blob).get("to")
for example;
Let me know if it's enough or if you are interested also in the type adapter solution.
How to parse JSON if an element is coming as jsonobject sometime and jsonarray sometime
Found the solution here
Gson handle object or array
@Pasupathi your solution is also correct but I want a way using Gson as my service response is too large and complex.
A value of a json str sometimes is a String, sometimes is a object, how could i use gson to parse it
To simplyfy android development, we can ask for the backend developers to change the Mobile API.The new API could returen the json string that cann't change the value.The value of all keys cann't sometimes be a string, sometimes a object.
Related Topics
How to Avoid Type Safety Warnings with Hibernate Hql Results
How to Monitor Java Memory Usage
Java 'File.Delete()' Is Not Deleting Specified File
Get Yesterday's Date Using Date
Performance of Stringtokenizer Class VS. String.Split Method in Java
Exclude @Component from @Componentscan
Overloading in Java and Multiple Dispatch
Adding Chartpanel to Jtabbedpane Using JPAnel
Java Regex for Support Unicode
Converting List<Integer> to List<String>
How Does Java's Preparedstatement Work
Deserialize JSON to Arraylist<Pojo> Using Jackson
Copy All Values from Fields in One Class to Another Through Reflection
Lambdaconversionexception with Generics: Jvm Bug