In this article we will compare custom object creation capabilities (such as Multi args constructor, static factory method) of JSON binding libraries in java i.e. Jackson vs. Gson vs. JSON-B (Eclipse Yasson)
For complete comparison of top JSON libraries & their features, visit below article.
Lets take example of Employee object which has –
- Multiple argument constructor for properties & no setter for properties i.e. only getters.
- Public static factory method for creating instance & private default constructor.
Jackson
In jackson, for both cases, we can mark constructor or factory method with annotation as shown below to notify Jackson to use multi args constructor to instantiate Employee object.
Args constructor annotated with @JsonCreator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; public class EmployeeArgConstructor { private String name; private String middleName; private String lastName; @JsonCreator public EmployeeArgConstructor(@JsonProperty("middleName") String middleName, @JsonProperty("name") String name, @JsonProperty("lastName") String lastName) { System.out.println("Argument Constructor called - " + name + " " + middleName + " " + lastName); this.name = name; this.middleName = middleName; this.lastName = lastName; } ... getters ... } |
Factory method annotated with @JsonCreator
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 |
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; public class EmployeeFactoryMethod { private String name; private String middleName; private String lastName; // Private Constructor private EmployeeFactoryMethod() { } // Factory method @JsonCreator public static EmployeeFactoryMethod getInstance(@JsonProperty("middleName") String middleName, @JsonProperty("name") String name, @JsonProperty("lastName") String lastName) { System.out.println("Factory method called - " + name + " " + middleName + " " + lastName); EmployeeFactoryMethod employeeFactoryMethod = new EmployeeFactoryMethod(); employeeFactoryMethod.name = name; employeeFactoryMethod.middleName = middleName; employeeFactoryMethod.lastName = lastName; return employeeFactoryMethod; } ... getters ... } |
Parse JSON using both above POJOs in jackson.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import java.io.IOException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; public class JacksonCustomObjectCreation { public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException { String employeeJson = "{\"middleName\":\"C\",\"name\":\"Jimmy\",\"lastName\":\"Kimmel\"}"; ObjectMapper mapper = new ObjectMapper(); // Args constructor EmployeeArgConstructor employee = mapper.readValue(employeeJson, EmployeeArgConstructor.class); System.out.println("EmployeeArgConstructor = " + employee.getName() + " " + employee.getMiddleName() + " " + employee.getLastName()); // Factory or instance creation method EmployeeFactoryMethod employee_1 = mapper.readValue(employeeJson, EmployeeFactoryMethod.class); System.out.println("EmployeeFactoryMethod = " + employee_1.getName() + " " + employee_1.getMiddleName() + " " + employee_1.getLastName()); } } |
1 2 3 4 |
Argument Constructor called - Jimmy C Kimmel EmployeeArgConstructor = Jimmy C Kimmel Factory method called - Jimmy C Kimmel EmployeeFactoryMethod = Jimmy C Kimmel |
GSON
Lets take same employee example as above. GSON does not have any specific out of the box annotation for this. But by default, GSON ignores constructor in such cases & uses reflection to instantiate object. This might be problematic as you might not notice this & assume constructor is being used.
You can specifically write custom JsonDeserializer to achieve the same.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class EmployeeArgConstructor { private String name; private String middleName; private String lastName; public EmployeeArgConstructor(String middleName, String name, String lastName) { System.out.println("Argument Constructor called - " + name + " " + middleName + " " + lastName); this.name = name; this.middleName = middleName; this.lastName = lastName; } ... getters ... } |
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 |
import java.lang.reflect.Type; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; public class GsonCustomObjectCreation { public static void main(String[] args) { String employeeJson = "{\"middleName\":\"C\",\"name\":\"Jimmy\",\"lastName\":\"Kimmel\"}"; // Uses reflection. Does not call args constructor. So might be defective based // on importance of constructor. Gson basicGson = new Gson(); EmployeeArgConstructor employee = basicGson.fromJson(employeeJson, EmployeeArgConstructor.class); System.out.println(employee.getName() + " " + employee.getMiddleName() + " " + employee.getLastName()); // Use custom JsonDeserializer Gson customGson = new GsonBuilder() .registerTypeAdapter(EmployeeArgConstructor.class, new EmployeeArgConstructorInstanceCreator()) .create(); EmployeeArgConstructor employee_1 = customGson.fromJson(employeeJson, EmployeeArgConstructor.class); System.out.println(employee_1.getName() + " " + employee_1.getMiddleName() + " " + employee_1.getLastName()); } } class EmployeeArgConstructorInstanceCreator implements JsonDeserializer<EmployeeArgConstructor> { @Override public EmployeeArgConstructor deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (EmployeeArgConstructor.class.equals(typeOfT)) { return new EmployeeArgConstructor(json.getAsJsonObject().get("middleName").getAsString(), json.getAsJsonObject().get("name").getAsString(), json.getAsJsonObject().get("lastName").getAsString()); } return null; } } |
1 2 3 |
Jimmy C Kimmel Argument Constructor called - Jimmy C Kimmel Jimmy C Kimmel |
JSON-B
JSONB does have annotations similar to Jackson as shown below. JSON-B annotation support both argument constructor as well as public static factory methods for instance creation.
Args constructor annotated with @JsonbCreator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import javax.json.bind.annotation.JsonbCreator; import javax.json.bind.annotation.JsonbProperty; public class EmployeeArgConstructor { private String name; private String middleName; private String lastName; @JsonbCreator public EmployeeArgConstructor(@JsonbProperty("middleName") String middleName, @JsonbProperty("name") String name, @JsonbProperty("lastName") String lastName) { System.out.println("Argument Constructor called - " + name + " " + middleName + " " + lastName); this.name = name; this.middleName = middleName; this.lastName = lastName; } ... getters ... } |
Factory method annotated with @JsonbCreator
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 |
import javax.json.bind.annotation.JsonbCreator; import javax.json.bind.annotation.JsonbProperty; public class EmployeeFactoryMethod { private String name; private String middleName; private String lastName; // Private Constructor private EmployeeFactoryMethod() { } // Factory method @JsonbCreator public static EmployeeFactoryMethod getInstance(@JsonbProperty("middleName") String middleName, @JsonbProperty("name") String name, @JsonbProperty("lastName") String lastName) { System.out.println("Factory method called - " + name + " " + middleName + " " + lastName); EmployeeFactoryMethod employeeFactoryMethod = new EmployeeFactoryMethod(); employeeFactoryMethod.name = name; employeeFactoryMethod.middleName = middleName; employeeFactoryMethod.lastName = lastName; return employeeFactoryMethod; } ... getters ... } |
Now lets parse JSON using above both POJOs in JSONB.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; public class JsonBCustomObjectCreation { public static void main(String[] args) { String employeeJson = "{\"middleName\":\"C\",\"name\":\"Jimmy\",\"lastName\":\"Kimmel\"}"; Jsonb jsonb = JsonbBuilder.create(); // Args constructor EmployeeArgConstructor employee = jsonb.fromJson(employeeJson, EmployeeArgConstructor.class); System.out.println(employee.getName() + " " + employee.getMiddleName() + " " + employee.getLastName()); // Factory or instance method EmployeeFactoryMethod employee_1 = jsonb.fromJson(employeeJson, EmployeeFactoryMethod.class); System.out.println(employee_1.getName() + " " + employee_1.getMiddleName() + " " + employee_1.getLastName()); } } |
1 2 3 4 |
Argument Constructor called - Jimmy C Kimmel Jimmy C Kimmel Factory method called - Jimmy C Kimmel Jimmy C Kimmel |