How to Write a Custom JSON Deserializer for Gson

How do I write a custom JSON deserializer for Gson?

@Override
public User deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {

JsonObject jobject = json.getAsJsonObject();

return new User(
jobject.get("id").getAsInt(),
jobject.get("name").getAsString(),
new Timestamp(jobject.get("update_date").getAsLong()));
}

I'm assuming User class has the appropriate constructor.

Custom JSON deserializer using Gson

You have to write a custom deserializer. I'd do something like this:

First you need to include a new class, further than the 2 you already have:

public class Response {
public VkAudioAlbumsResponse response;
}

And then you need a custom deserializer, something similar to this:

private class VkAudioAlbumsResponseDeserializer 
implements JsonDeserializer<VkAudioAlbumsResponse> {

@Override
public VkAudioAlbumsResponse deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {

JsonArray jArray = (JsonArray) json;

int firstInteger = jArray.get(0); //ignore the 1st int

VkAudioAlbumsResponse vkAudioAlbumsResponse = new VkAudioAlbumsResponse();

for (int i=1; i<jArray.size(); i++) {
JsonObject jObject = (JsonObject) jArray.get(i);
//assuming you have the suitable constructor...
VkAudioAlbum album = new VkAudioAlbum(jObject.get("owner_id").getAsInt(),
jObject.get("album_id").getAsInt(),
jObject.get("title").getAsString());
vkAudioAlbumsResponse.getResponse().add(album);
}

return vkAudioAlbumsResponse;
}
}

Then you have to deserialize your JSON like:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(VkAudioAlbumsResponse.class, new VkAudioAlbumsResponseDeserializer());
Gson gson = gsonBuilder.create();
Response response = gson.fromJson(jsonString, Response.class);

With this approach, when Gson tries to deserialize the JSON into Response class, it finds that there is an attribute response in that class that matches the name in the JSON response, so it continues parsing.

Then it realises that this attribute is of type VkAudioAlbumsResponse, so it uses the custom deserializer you have created to parse it, which process the remaining portion of the JSON response and returns an object of VkAudioAlbumsResponse.

Note: The code into the deserializer is quite straightforward, so I guess you'll have no problem to understand it... For further info see Gson API Javadoc

Gson custom deserializer just on field

Here is the code for custom deserialization (for Gender) with sample invocation.

1) Gender deserializer is case insensitive

2) Invalid values handled (i.e. input json contains other than male and female)

Main method:-

public static void main(String[] args) {
String jsonString = "{'firstName' : 'john','lastName' : 'stones','gender' : 'male'}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(Person.class, new PersonModelDeserializer())
.create();

Person person = gson.fromJson(jsonString, Person.class);
System.out.println(person.toString());

}

Gender Enum:-

public enum Gender {
MALE, FEMALE
}

Deserializer:-

If the input json doesn't have male or female, the valueOf method throws IllegalArgumentException which has been handled as well.

The Gender deserialization is case insensitive as well.

public class PersonModelDeserializer implements JsonDeserializer<Person> {

@Override
public Person deserialize(JsonElement paramJsonElement, Type paramType,
JsonDeserializationContext paramJsonDeserializationContext) throws JsonParseException {

Person person = new Gson().fromJson(paramJsonElement.getAsJsonObject(), Person.class);

try {
Gender gender = null;
if (paramJsonElement.getAsJsonObject().get("gender") != null) {
gender = Gender.valueOf(paramJsonElement.getAsJsonObject().get("gender").getAsString().toUpperCase());
}

person.setGender(gender);
} catch (IllegalArgumentException ie) {
System.out.println(ie.getMessage());
System.out.println("Gender cannot be serialized ..");
}

return person;
}

}

Person class:-

public class Person implements Serializable{
private static final long serialVersionUID = 5447375194275313051L;

private String firstName;
private String lastName;

private Gender gender;

... getters and setters
}

How do I write a custom JSON deserializer for Gson?

@Override
public User deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {

JsonObject jobject = json.getAsJsonObject();

return new User(
jobject.get("id").getAsInt(),
jobject.get("name").getAsString(),
new Timestamp(jobject.get("update_date").getAsLong()));
}

I'm assuming User class has the appropriate constructor.

How to make a custom list deserializer in Gson?

This is what I came up with:

class CustomListConverter implements JsonDeserializer<CustomList<?>> {
public CustomList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) {
Type valueType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];

CustomList<Object> list = new CustomList<Object>();
for (JsonElement item : json.getAsJsonArray()) {
list.add(ctx.deserialize(item, valueType));
}
return list;
}
}

Register it like this:

Gson gson = new GsonBuilder()
.registerTypeAdapter(CustomList.class, new CustomListConverter())
.create();

Error deserializing JSON response with custom Gson deserializer

Oh, it's a very common mistake. You have to create parameterized types with TypeToken.getParameterized. So you have to change object : TypeToken<List<Item>>() {}.type to TypeToken.getParameterized(List::class.java, Item::class.java).type

class ItemsDeserializer : JsonDeserializer<List<Item>> {

override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): List<Item> {

val items: JsonElement = json!!.asJsonObject.get("items")
val listType= TypeToken.getParameterized(List::class.java, Item::class.java).type

return Gson().fromJson(items, listType)
}
}
private val gson = GsonBuilder()
.registerTypeAdapter(TypeToken.getParameterized(List::class.java, Item::class.java).type, ItemsDeserializer())
.create()

Can I apply a custom deserializer to within another custom deserializer for GSON

The method you're looking for is JsonDeserializationContext.deserialize(). Per the warning about how to cause an infinite loop, this invokes any relevant custom deserializers you've set up.

I believe replacing the initialization of subItems inside the loop with a one-liner MySubItems subItems = context.deserialize(element, MySubItems.class); will do the trick, leaving only that and the check around list.add(subItems) in the loop body.

Gson custom deserialization

You'll need a custom deserializer (http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/JsonDeserializer.html), something like:

  public static class MyJsonAdapter implements JsonDeserializer<List<Person>>
{
List<Person> people = new ArrayList<>();
public List<Person> deserialize( JsonElement jsonElement, Type type, JsonDeserializationContext context )
throws JsonParseException
{
for (each element in the json data array)
{
Person p = context.deserialize(jsonElementFromArray,Person.class );
people.add(p);
}
}
return people;
}


Related Topics



Leave a reply



Submit