I was trying to learn the concept of inheritance and deserialization of java beans through Gson framework. Details regarding java bean classes and json files are given below.
ParentBean.java
public class ParentBean {
protected String key1;
protected String key2;
public ParentBean(String key1, String key2) {
super();
this.key1 = key1;
this.key2 = key2;
}
}
Bean1.java
public class Bean1 extends ParentBean {
private String key3;
public Bean1(String key1, String key2, String key3) {
super(key1, key2);
this.key3 = key3;
}
}
Bean2.java
public class Bean2 extends ParentBean {
private String key4;
public Bean2(String key1, String key2, String key4) {
super(key1, key2);
this.key4 = key4;
}
}
bean1.json
{
"key1":"value1",
"key2":"value2",
"key3":"value33"
}
bean2.json
{
"key1":"value1",
"key2":"value2",
"key4":"value43"
}
To explore things about inheritance and deserialization, I have used the following code:
Usage.java
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.google.gson.typeadapters.RuntimeTypeAdapterFactory;
public class Usage {
public static void main(String[] args) throws FileNotFoundException {
RuntimeTypeAdapterFactory<ParentBean> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(ParentBean.class, "type")
.registerSubtype(Bean1.class, "bean1")
.registerSubtype(Bean2.class, "bean2");
Gson gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create();
FileReader fr = new FileReader("bean1.json");
Type pType = new TypeToken<ParentBean>(){}.getType();
ParentBean pb = gson.fromJson(fr, pType);
if (pb instanceof Bean1) {
System.out.println(" Bean1");
} else if (pb instanceof Bean2) {
System.out.println("Bean2");
}
}
}
I am getting an error stack which is as follows:
Exception in thread "main" com.google.gson.JsonParseException: cannot deserialize class inheritance.ParentBean because it does not define a field named type
at com.google.gson.typeadapters.RuntimeTypeAdapterFactory$1.read(RuntimeTypeAdapterFactory.java:205)
at com.google.gson.TypeAdapter$1.read(TypeAdapter.java:199)
at com.google.gson.Gson.fromJson(Gson.java:795)
at com.google.gson.Gson.fromJson(Gson.java:761)
at inheritance.Usage.main(Usage.java:23)
In search of finding solution, I came across this stack overflow discussion. Unfortunately the discussion was about create() method. Error stack says the problem was with line 23 and this line contains fromJson()
method.
You need to tell gson more about the types. When serializing also the type needs to be serialized. So as the first comment by Jacob G. suggests you need the type field:
Docs for RuntimeTypeAdapterFactory.of(Class<T> baseType, String typeFieldName)
states:
Creates a new runtime type adapter using for baseType using typeFieldName as the type field name. Type field names are case sensitive.
Add it to your ParentBean
:
// Init it for serializing
// You used values like 'bean1' & 'bean2' but using class name is more generic
protected String type = getClass().getName();
According to above changes in bean type names change the building of RuntimeTypeAdapterFactory
accordingly:
RuntimeTypeAdapterFactory<ParentBean> runtimeTypeAdapterFactory =
RuntimeTypeAdapterFactory
.of(ParentBean.class, "type") // typeFieldName
.registerSubtype(Bean1.class, Bean1.class.getName())
.registerSubtype(Bean2.class, Bean2.class.getName());
Finally - when de-serailizing - the Json files need also the type information which will be serialized from the field type
so add it also fro both beans Json with correct package name:
"type":"org.example.gson.runtime.Bean1",
and
"type":"org.example.gson.runtime.Bean2",
You do not need to explicitly add a type field to your beans. As pirho suggests, you need to have the type field in your json string. If you're crafting this by hand, just add the type field, e.g.
{
"type":"bean1",
"key1":"value1",
"key2":"value2",
"key3":"value33"
}
Presumably, you're also serializing your objects and to achieve that, you'll need to specify the base class during serialization.
As swankjesse points out in
https://github.com/google/gson/issues/712
Try replacing this:
final String jsonStr = mGson.toJson(new Child());
With this:
final String jsonStr = mGson.toJson(new Child(), Base.class);
Then, when you serialize your types, your output json will be
{
"type":"bean1",
"key1":"value1",
"key2":"value2",
"key3":"value33"
}
That way, your beans can stay pure, without knowing that they are providing some type key to be used in serialization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With