This tutorial shows you how to create GSON Annotations Example using JsonAdapter. The @JsonAdapter annotation is one of userful GSON annotations. This annotation be used at field or class level to specify the Gson TypeAdapter for serializing or deserializing.
GSON converts java classes to JSON using its built-in type adapters by default. Sometimes, They doesn’t fit your specified requirements. So you can customize them by implementing custom type adapters.
Other interesting posts you may like
Table of contents:
1. Project structure
2. Create POJO
3. Create custom type adapter
4. Create Main class
Project structure
We will create project with structure like this:
Create POJO
First, we need to create POJO which is annotated with custom type adapter. It looks like below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package com.javabycode.jackson; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gson.annotations.JsonAdapter; @JsonAdapter(MyCustomTypeAdapter.class) public class Fruit { private String name; private double cost; private Date importedDate; private String flavor; private List<String> produceby = new ArrayList<String>(); private Map<String, Object> fruitPackage = new HashMap<String, Object>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public double getCost() { return cost; } public void setCost(double cost) { this.cost = cost; } public List<String> getProduceby() { return produceby; } public void setProduceby(List<String> produceby) { this.produceby = produceby; } public Date getImportedDate() { return importedDate; } public void setImportedDate(Date importedDate) { this.importedDate = importedDate; } public String getFlavor() { return flavor; } public void setFlavor(String flavor) { this.flavor = flavor; } public Map<String, Object> getFruitPackage() { return fruitPackage; } public void setFruitPackage(Map<String, Object> fruitPackage) { this.fruitPackage = fruitPackage; } @Override public String toString() { return "Fruit [name=" + name + ", cost=" + cost + ", importedDate=" + importedDate + ", flavor=" + flavor + ", produceby=" + produceby + ", fruitPackage=" + fruitPackage + "]"; } } |
As you can see, the above Fruit POJO is annotated with MyCustomTypeAdapter using @JsonAdapter annotation.
Now, we’re starting to create our custom type adapter named MyCustomTypeAdapter. The MyCustomTypeAdapter class looks like below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
package com.javabycode.jackson; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; public class MyCustomTypeAdapter extends TypeAdapter<Fruit> { private static SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); @Override public void write(JsonWriter writer, Fruit fruit) throws IOException { writer.beginObject(); writer.name("name").value(fruit.getName()); writer.name("importedDate").value(sdf.format(fruit.getImportedDate())); writer.name("flavor").value(fruit.getFlavor()); //Customize our own serialization with adding 10% VAT double costVAT = fruit.getCost() + 0.1 * fruit.getCost(); writer.name("cost").value(costVAT); writer.name("produceby"); writer.beginArray(); for (String produce : fruit.getProduceby()) { writer.value(produce); } writer.endArray(); writer.name("fruitPackage"); writer.beginObject(); for (Entry<String, Object> entry : fruit.getFruitPackage().entrySet()) { if (entry.getKey() == "package") { writer.name("package").value(entry.getValue().toString()); } else if (entry.getKey() == "quantityNet") { writer.name("quantityNet").value(entry.getValue().toString()); } } writer.endObject(); writer.endObject(); writer.close(); } @Override public Fruit read(JsonReader reader) throws IOException { Fruit fruit = new Fruit(); reader.beginObject(); while (reader.hasNext()) { String name = reader.nextName(); if (name.equals("name")) { fruit.setName(reader.nextString()); } else if (name.equals("cost")) { fruit.setCost(reader.nextInt()); } else if (name.equals("importedDate")) { try { fruit.setImportedDate(sdf.parse(reader.nextString())); } catch (ParseException e) { // TODO something e.printStackTrace(); } } else if (name.equals("flavor")) { fruit.setFlavor(reader.nextString()); } else if (name.equals("cost")) { double cost = reader.nextDouble(); //Customize our own deserialization with excluding 10% VAT double costNotVAT = cost / 1.1; fruit.setCost(costNotVAT); } else if (name.equals("produceby") && reader.peek() != JsonToken.NULL) { fruit.setProduceby(readStringArray(reader)); } else if (name.equals("fruitPackage") && reader.peek() != JsonToken.NULL) { fruit.setFruitPackage(readObject(reader)); } else { reader.skipValue(); } } reader.endObject(); return fruit; } public List<String> readStringArray(JsonReader reader) throws IOException { List<String> colors = new ArrayList<String>(); reader.beginArray(); while (reader.hasNext()) { colors.add(reader.nextString()); } reader.endArray(); return colors; } public Map<String, Object> readObject(JsonReader reader) throws IOException { Map<String, Object> pkg = new HashMap<String, Object>(); reader.beginObject(); while (reader.hasNext()) { String name = reader.nextName(); if (name.equals("package")) { pkg.put("package", reader.nextString()); } else if (name.equals("quantityNet")) { pkg.put("quantityNet", reader.nextString()); } } reader.endObject(); return pkg; } } |
Above is a custom type adapter which extends TypeAdapter class and we simply overrides read and write methods. These methods are used for serialization and deserialization.
These read and write methods are implemented with token based approach. During deserialization process, we read token and iterate over them one by one using JsonReader’s hasNext and nextName methods. One more thing, We must process opening and closing brackets using beginObject and endObject (or beginArray/endArray) methods.
Notices that @JsonAdapter annotation can be used on both class and field level. In this GSON Annotations Example using JsonAdapter, we are using it on class level.
Finally, we create main class for this GSON Annotations Example using JsonAdapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.javabycode.jackson; import java.util.Date; import com.google.gson.Gson; public class GsonJsonAdapterAnnotationExample { public static void main(String args[]) { Gson gson = new Gson(); Fruit fruit = new Fruit(); fruit.setName("Banana"); fruit.setCost(10); fruit.setFlavor(""); fruit.setImportedDate(new Date()); fruit.getProduceby().add("Indonesia"); fruit.getProduceby().add("Canada"); fruit.getProduceby().add("Cuba"); fruit.getFruitPackage().put("package", "Fruit Punnets"); fruit.getFruitPackage().put("quantityNet", 5); /* Serialization */ System.out.println("Before serializing JSON string using GSON annotation"); System.out.println(fruit); System.out.println("After serializing JSON string using GSON annotation"); String serializedJson = gson.toJson(fruit); System.out.println(serializedJson); /* Deserialization */ System.out.println("Deserializing JSON string using GSON annotation"); Fruit newFruit = gson.fromJson(serializedJson, Fruit.class); System.out.println(newFruit); } } |
Running the above main class on Eclipse, we get the output like this:
Notices that we can register our custom type adapter with GsonBuilder instead of using this annotation @JsonAdapter. This registration looks like below:
1 |
Gson gson = new GsonBuilder().registerTypeAdapter(Fruit.class, new MyCustomTypeAdapter()).create(); |
And we will get same result as annotation usage.
That’s it. Hope that this GSON Annotations Example using JsonAdapter tutorial helps you create your own custom type adapter.
Download source code, click link below
Gson-JsonAdapter-Annotation-Example.zip