Failed to deserialize an object having List using Gson - java

I am trying to deserialize a JSON string into a Java object but I'm getting the following exception:
The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#306a30c7 failed to deserialized json object ["93"] given the type class java.util.ArrayList
My main file:
public static void main(String[] args) throws IOException {
String s = "{\"intents\":[\"93\"],\"_id\":\"frequestQueries\"}";
Gson gson = new Gson();
JavaObject a = gson.fromJson(s, JavaObject.class);
System.out.println(a);
The class which should represent the JSON is:
public class JavaObject {
public ArrayList intents;
private String _id ;
public JavaObject(){
intents = new ArrayList();
intents.add(93);
_id = "frequestQueries";
}
}
I tried the solutions given on SO with similar exceptions but I'm not able to figure out the exact reason. I tried this one but seems no concrete solution is given. My Gson version is 1.4.
Edit: I have updated my Gson to 2.8.0 and the exception went away.

I'm posting solution to my own question since many people using Arraylist in Gson deserialization like this one were suffering with the same problem. Just update Gson version and it will take care of Gson deserialization issue.

Related

How to parse inner json string as nested class using retrofit

I have used retrofit with nested classes before, but the current api I'm tring to use has such a structure:
Request body:
{
"Id" : "a2",
"messageCode" : 1,
"bigNestedClass" : "{\"field1\":238,\"otherField\":246,\"ip\":\"10.255.130.154\",\"someOtherField\":15,\"Info\":1501069568}"
}
and a similar response body.
Note that bigNestedClass is a string.
I created different pojo classes for request and response. However, creating a nested BigNestedClass makes this field filled as JSON object, as expected, not a JSON string. And I have same problem parsing the response too.
My question: Is there a way in retrofit that enables encode, parse nested classes as strings?
I use Retrofit 2.0
I use gson (can be changed)
With gson I would simply make this with TypeAdapter. See the class below:
public class MyTypeAdapter extends TypeAdapter<BigNestedClass> {
private Gson gson = new Gson();
#Override
public BigNestedClass read(JsonReader arg0) throws IOException {
// Get the string value and do kind of nested deserializing to an instance of
// BigNestedClass
return gson.fromJson(arg0.nextString(), BigNestedClass.class);
}
#Override
public void write(JsonWriter arg0, BigNestedClass arg1) throws IOException {
// Get the instance value and insted of normal serializing make the written
// value to be a string having escaped json
arg0.value(gson.toJson(arg1));
}
}
Then you would only need to register MyTypeAdapter with gson, like:
private Gson gson = new GsonBuilder()
.registerTypeAdapter(BigNestedClass.class, new MyTypeAdapter())
.create();
To have retrofit to use it you would need to do just a bit more when creating it:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

How to get object from json string, for Java class containing a list, generated using Immutables?

my use case included a similar class, using immutables (https://immutables.github.io/) :
#Value.Immutable
#Gson.TypeAdapters
public abstract class SampleClass {
public abstract String var1();
public abstract String var2();
public abstract Date date1();
}
I was converting it to Json string,using gson, and then getting the object again using :
SampleClass obj1 = new Gson().fromJson("generated_json_string",ImmutableSampleClass.class);
But now I had to change var1 to List of String, and now I am getting :
java.lang.RuntimeException: Failed to invoke com.google.common.collect.ImmutableList() with no args
What is the correct way to get the object from JSON string ?
Figured out what was wrong, hence answering the question.
Immutables generated class GsonAdaptersSampleClass, which implements TypeAdapterFactory. Using this and with the help of this answer : https://stackoverflow.com/a/13624060/3192744
I could find the following correct way to deserialize JSON string:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapterFactory(new GsonAdaptersSampleClass());
Gson gson = gsonBuilder.create();
SampleClass obj1 = gson.fromJson("generated_json_string",ImmutableSampleClass.class);
You can try GSON library for converting JSON to your existing class object.
http://howtodoinjava.com/best-practices/google-gson-tutorial-convert-java-object-to-from-json/

GSON treat part of JSON as String [duplicate]

I am trying to deserialize a json object into a java bean.
The main issue I am facing is that I'd like to treat the field object of the json string as a plain string, even if it contains a potentially correct json object.
The json structure is like this:
{
"type":"user",
"object":{
"id":"1",
...}
}
How can i tell gson to ignore the object value so that it doesn't get deserialized into an object? I'd like it only to be mapped to a plain String field in my bean so that I can dispose a proper deserialization for it, once I got the type from the type field.
Just declare it as of type JsonObject
class ExampleJsonModel {
#SerializedName("type")
public String type;
#SerializedName("object")
public JsonObject object;
}
I don't know if your problem is solved. I ran into a similar question and here it is how I worked it out:
JsonDeserializer allows you to make you own adapter to deserialize that **:
class JavaBeanDeserializer implements JsonDeserializer<JavaBeanObject>() {
public JavaBeanObject fromJson(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// return JavaBeanObject built using your logic.
}
You've to register JavaBeanDeserializer to Gson object when building it:
Gson gson = new GsonBuilder().registerTypeAdapter(JavaBeanObject.class, new JavaBeanDeserializer()).create();

deserialize json field into plain string with gson

I am trying to deserialize a json object into a java bean.
The main issue I am facing is that I'd like to treat the field object of the json string as a plain string, even if it contains a potentially correct json object.
The json structure is like this:
{
"type":"user",
"object":{
"id":"1",
...}
}
How can i tell gson to ignore the object value so that it doesn't get deserialized into an object? I'd like it only to be mapped to a plain String field in my bean so that I can dispose a proper deserialization for it, once I got the type from the type field.
Just declare it as of type JsonObject
class ExampleJsonModel {
#SerializedName("type")
public String type;
#SerializedName("object")
public JsonObject object;
}
I don't know if your problem is solved. I ran into a similar question and here it is how I worked it out:
JsonDeserializer allows you to make you own adapter to deserialize that **:
class JavaBeanDeserializer implements JsonDeserializer<JavaBeanObject>() {
public JavaBeanObject fromJson(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// return JavaBeanObject built using your logic.
}
You've to register JavaBeanDeserializer to Gson object when building it:
Gson gson = new GsonBuilder().registerTypeAdapter(JavaBeanObject.class, new JavaBeanDeserializer()).create();

How to serialize a Map of a Map with GSON?

I want to serialize my Example class below into JSON using GSON.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.LinkedHashMap;
public class Example
{
private LinkedHashMap<String,Object> General;
private static final String VERSION="Version";
private static final String RANGE="Range";
private static final String START_TIME="Start_Time";
private static final String END_TIME="End_Time";
public Example() {
General = new LinkedHashMap<String,Object>();
General.put(VERSION, "0.1");
LinkedHashMap<String,String> Range = new LinkedHashMap<String, String>();
Range.put(START_TIME, "now");
Range.put(END_TIME, "never");
General.put(RANGE, Range);
}
public String toJSON() {
Gson gson = new GsonBuilder().serializeNulls().create();
return gson.toJson(this);
}
}
I expected to get the following output:
{"General":{"Version":"0.1","Range":{"Start_Time":"now","End_Time":"never"}}}
But calling the function toJSON() returns
{"General":{"Version":"0.1","Range":{}}}
It seems that GSON cannot serialize the Map Range inside the Map General. Is this a limitation of GSON or am I doing something wrong here?
The reason why Nishant's answer works is because Gson's default constructor enables all kind of stuff per default that you would otherwise have to manually enably using the GsonBuilder.
From the JavaDocs:
Constructs a Gson object with default configuration. The default configuration has the following settings:
The JSON generated by toJson methods is in compact representation. This means that all the unneeded white-space is removed. You can change this behavior with GsonBuilder.setPrettyPrinting().
The generated JSON omits all the fields that are null. Note that nulls in arrays are kept as is since an array is an ordered list. Moreover, if a field is not null, but its generated JSON is empty, the field is kept. You can configure Gson to serialize null values by setting GsonBuilder.serializeNulls().
Gson provides default serialization and deserialization for Enums, Map, java.net.URL, java.net.URI, java.util.Locale, java.util.Date, java.math.BigDecimal, and java.math.BigInteger classes. If you would prefer to change the default representation, you can do so by registering a type adapter through GsonBuilder.registerTypeAdapter(Type, Object).
The default Date format is same as java.text.DateFormat.DEFAULT. This format ignores the millisecond portion of the date during serialization. You can change this by invoking GsonBuilder.setDateFormat(int) or GsonBuilder.setDateFormat(String).
By default, Gson ignores the com.google.gson.annotations.Expose annotation. You can enable Gson to serialize/deserialize only those fields marked with this annotation through GsonBuilder.excludeFieldsWithoutExposeAnnotation().
By default, Gson ignores the com.google.gson.annotations.Since annotation. You can enable Gson to use this annotation through GsonBuilder.setVersion(double).
The default field naming policy for the output Json is same as in Java. So, a Java class field versionNumber will be output as "versionNumber#quot; in Json. The same rules are applied for mapping incoming Json to the Java classes. You can change this policy through GsonBuilder.setFieldNamingPolicy(FieldNamingPolicy).
By default, Gson excludes transient or static fields from consideration for serialization and deserialization. You can change this behavior through GsonBuilder.excludeFieldsWithModifiers(int).
OK, now I see what the problem is. The default Map serializer, as you expected, does not support nested maps. As you can see in this source snippet from DefaultTypeAdapters (especially if you step through with a debugger) the variable childGenericType is set to the type java.lang.Object for some mysterious reason, so the runtime type of the value is never analysed.
Two solutions, I guess:
Implement your own Map serializer / deserializer
Use a more complicated version of your method, something like this:
public String toJSON(){
final Gson gson = new Gson();
final JsonElement jsonTree = gson.toJsonTree(General, Map.class);
final JsonObject jsonObject = new JsonObject();
jsonObject.add("General", jsonTree);
return jsonObject.toString();
}
Try this:
Gson gson = new Gson();
System.out.println(gson.toJson(General));
Not sure if you're still looking for a solution, this works for me:
import java.util.LinkedHashMap;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Example {
// private static LinkedHashMap<String,Object> General;
private ImmutableMap General;
private static final String VERSION="Version";
private static final String RANGE="Range";
private static final String START_TIME="Start_Time";
private static final String END_TIME="End_Time";
public Example() {
LinkedHashMap<String,String> Range = new LinkedHashMap<String, String>();
Range.put(START_TIME, "now");
Range.put(END_TIME, "never");
// General.put(RANGE, Range);
General = ImmutableMap.of(VERSION, "0.1", RANGE, Range);
}
public String toJSON() {
// Gson gson = new GsonBuilder().serializeNulls().create();
Gson gson = new Gson();
return gson.toJson(this);
}
}
returns: {"General":{"Version":"0.1","Range":{"Start_Time":"now","End_Time":"never"}}}
Obviously you could use ImmutableMap.copyOf(your_hashmap)here instead
A simpler alternative would be to use Jackson instead of GSON, serialization of a nested map works out of the box:
LinkedHashMap<String, Object> general;
general = new LinkedHashMap<String, Object>();
general.put("Version", "0.1");
LinkedHashMap<String, String> Range = new LinkedHashMap<String, String>();
Range.put("Start_Time", "now");
Range.put("End_Time", "never");
general.put("Range", Range);
// Serialize the map to json using Jackson
ByteArrayOutputStream os = new ByteArrayOutputStream();
new org.codehaus.jackson.map.ObjectMapper().writer().writeValue(os,
general);
String json = os.toString();
os.close();
System.out.println(json);
Output:
{"Version":"0.1","Range":{"Start_Time":"now","End_Time":"never"}}

Categories

Resources