Reputation: 47965
Maybe I'm running in the wrong direction, but I have a list of elements which I want to read.
I have an abstract base class let's call it Person
:
public abstract class Person {
public int id;
public String name;
}
Now I have two possible implementations:
public class Hunter implements Person {
public int skill;
// and some more stuff
}
public class Zombie implements Person {
public int uglyness;
// and some more stuff
}
Now I have this example JSON:
[
{"id":1, "type":"zombie", "name":"Ugly Tom", "uglyness":42},
{"id":2, "type":"hunter", "name":"Shoot in leg Joe", "skill":0}
]
How can I read this JSON as List<Person>
?
I'm playing for a while with TypeAdapterFactory
and tried to use a class called CustomizedTypeAdapterFactory
since my real structure is a little more complex as the funny example above.
I ended in that I want to delegate the serialization with this call:
return gson.getDelegateAdapter(this, resultType);
However I have no idea how I can create at runtime that TypeToken<T>
which is required for this call. Any ideas?
Upvotes: 3
Views: 2734
Reputation: 93872
How can I read this JSON as List?
One possibility would be to create a custom deserializer that acts like a factory.
The first step would be to define this deserializer
class PersonJsonDeserializer implements JsonDeserializer<Person> {
@Override
public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String type = json.getAsJsonObject().get("type").getAsString();
switch(type) {
case "zombie":
return context.deserialize(json, Zombie.class);
case "hunter":
return context.deserialize(json, Hunter.class);
default:
throw new IllegalArgumentException("Neither zombie or hunter");
}
}
}
It fetches the value associated with the key "type" and choose the proper type to deserialize the object you're currently reading.
Then, you need to plug this deserializer within the parser.
public class GsonTest {
public static void main(String[] args) {
String json = "[\n" +
" {\"id\":1, \"type\":\"zombie\", \"name\":\"Ugly Tom\", \"uglyness\":42},\n" +
" {\"id\":2, \"type\":\"hunter\", \"name\":\"Shoot in leg Joe\", \"skill\":0}\n" +
"]";
Gson gson = new GsonBuilder().registerTypeAdapter(Person.class, new PersonJsonDeserializer()).create();
Type type = new TypeToken<List<Person>>(){}.getType();
List<Person> list = gson.fromJson(json, type);
for(Person p : list) {
System.out.println(p);
}
}
}
Running it with your example, I get:
Zombie{id=1; name=Ugly Tom; uglyness=42}
Hunter{id=2; name=Shoot in leg Joe; skill=0}
If the value of the type already corresponds to the class name, you might want to use Class.forName
also:
class PersonJsonDeserializer implements JsonDeserializer<Person> {
@Override
public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String className = json.getAsJsonObject().get("type").getAsString();
className = Character.toUpperCase(className.charAt(0)) + className.substring(1);
try {
return context.deserialize(json, Class.forName(className));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
Upvotes: 7