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();
Related
There is already data present in S3 Bucket which needs to be processed.
Data Follow this particular Schema
{
"serviceId": "9e148371a96b9054e689d3669998ef54ff11cdd3cad9dfa395e4da4c5a50cbc4",
"serviceVersion": 2
}
POJO Classes
abstract class Service {
String serviceId;
int serviceVersion;
}
ServiceA extends Service {
String fieldX;
}
ServiceB extends Service {
}
Code
USing Gson to desrialize this data
Gson gson = new Gson();
String data = "{\n" +
" \"serviceId\": \"9e148371a96b9054e689d3669998ef54ff11cdd3cad9dfa395e4da4c5a50cbc4\",\n" +
" \"serviceVersion\": 2\n" +
"}";
Code fails with exception
Failed to invoke public com.*.transportation.*.Service()() with no args
How do i deserialize this data ?
Registering CustomType Adapters won't help either, since Data is already deserialized, and no Type information is available.
Only solution i can think of here, is to create customerTypeAdapter and decide the Type of Concrete class on the basis of
#Override
public Service deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (Object.nonNull(jsonObject.get("fieldX") {
return jsonDeserializationContext.deserialize(jsonElement,ServiceA.class);
}
jsonDeserializationContext.deserialize(jsonElement,ServiceB.class);
}
Please suggest some the alternative, if this can be solved in a better way?
I have provided this as an example, but the actual object is very large and consists of too many nested interface/abstract classes.
Data is already present in S3/Local Storage and no _type information is present.
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.
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/
I want to build a flexible api, I have no definite case sensitivity the user can pass, so GSON must be able to deserialize this in case sensitive.
{"firstName":"Juan"}
{"firstname":"Juan"}
{"Firstname":"Juan"}
...
How can I deserialize these field into my Foo's firstName?
public class Foo {
private String firstName;
//..getters
}
I tried to use FieldNamingPolicy, but it did not work.
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.setPrettyPrinting()
.create();
There is an easier way to almost get what you want. I'd not realised but since GSON 2.4 the SerializedName annotation supports an array of alternate names that GSON can deserialise from.
public class Foo {
#SerializedName(value = "firstName", alternate = {"firstname", "Firstname"})
private String firstName;
public java.lang.String getFirstName() {
return firstName;
}
#Override
public String toString() {
return "Foo:" + firstName;
}
}
You could use this to support the likely variations without being able to handle something like "firSTNAme".
https://google.github.io/gson/apidocs/com/google/gson/annotations/SerializedName.html
It doesn't look as though GSON has an easy built in way to customise it the way you want. It looks as though technically you could implement a TypeAdapterFactory that uses reflection to do this but it seems excessive.
You might open a feature request with GSON about introducing support for field naming strategies that support alternative names or pattern, or case insensitive matching.
Not a complete solution to this problem but if the keys happen to be all lowercase such as headimgurl then you can implement FieldNamingStrategy interface
Gson gson = new GsonBuilder().setFieldNamingStrategy(f -> f.getName().toLowerCase()).create();
to parse the value to headImgUrl field of the object.
I think you will need to implement a custom JsonDeserialiser.
The field naming policy and strategy appears to provide a way to map Java field names to JSON properties but not JSON properties to Java field names.
This deserialiser will ignore the case of the name of the property and try to match it against "firstname".
public final class FooDeserialiser implements JsonDeserializer<Foo> {
#Override
public Foo deserialize(
JsonElement jsonElement,
Type type,
JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException {
for (Map.Entry<String, JsonElement> property : jsonElement.getAsJsonObject().entrySet()) {
if ("firstname".equalsIgnoreCase(property.getKey())) {
return new Foo(property.getValue().getAsString());
}
}
// Or return null if you prefer, or return a Foo with null as the first name
// It has failed to find any property that looks like firstname
throw new JsonParseException("No firstName property");
}
}
This can be registered as a type adapter with the Gson object when it is being built like this:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(Foo.class, new FooDeserialiser())
.setPrettyPrinting()
.create();
And then invoked like:
final List<Foo> foos = gson.fromJson(
"[{\"firstName\":\"Juan\"},{\"firstname\":\"Juan\"},{\"Firstname\":\"Juan\"}]",
new TypeToken<List<Foo>>() {}.getType());
To return a list for Foo objects each with the first name Juan.
The only problem is building the deserialisers for your objects may become a burden. Your deserialisers will need to be more complicated than the above example.
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();