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
What's the Difference Between Fx:Id and Id: in Javafx
How to Deploy a Javafx 11 Desktop Application with a Jre
How to Get the Exact Middle of a Screen, Even When Re-Sized
What Exactly Is Field Injection and How to Avoid It
How to Get Current Moment in Iso 8601 Format with Date, Hour, and Minute
Difference Between Thread's Context Class Loader and Normal Classloader
Differencebetween Instanceof and Class.Isassignablefrom(...)
How to Convert Byte Size into a Human-Readable Format in Java
Differencebetween Thread.Start() and Thread.Run()
Using Prepared Statements to Set Table Name
Returning from a Finally Block in Java
File Upload Along with Other Object in Jersey Restful Web Service
When and How to Use a Threadlocal Variable
Why Is Spring's Applicationcontext.Getbean Considered Bad
When Should a Class Be Comparable And/Or Comparator
What Does the Java Assert Keyword Do, and When Should It Be Used
What's the Nearest Substitute for a Function Pointer in Java