This tutorial is about deserialization(convert JSON to Java object), for serialization(convert Java object to JSON), please visit
In a RESTful Web Service, we need a library to convert JSON request body to Java object
In Spring, we can configure FasterXML Jackson as the library
In the application context xml, add the following:
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> </mvc:message-converters> </mvc:annotation-driven>
In pom.xml, add the following dependency: (Using Gradle please see)
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.9</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency>
dependencies { implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.9' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9' }
Define a controller class with a @PostMapping annotated method with Customer object as the param:
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/myapp") public class MyController { @PostMapping("/customer") public void postCustomer(@RequestBody Customer customer) { System.out.println("name = " + customer.getName() + ", age = " + customer.getAge() + ", state = " + customer.getState() + ", zip = " + customer.getZip()); } }
And the Customer class:
public class Customer { private String name; private Integer age; private String state; private Integer zip; // public setter and getter of all the fields }
After building and deployed to <CONTEXT_PATH>, by calling
curl --request POST '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data ' { "name": "Sam", "age": 30, "state": "CA", "zip": 90008 }'
System.out.println will print:
name = Sam, age = 30, state = CA, zip = 90008
Customized field name could be used. If a field does not start with lower case letter or have special characters, for instance:
{"@Name!":"Sam","age":30,"state":"CA","zip":90008}
The @JsonProperty annotation could be used to change the field name. The Customer class will look like this:
public class Customer { @JsonProperty("@Name!") private String name; private Integer age; private String state; private Integer zip; // public setter and getter of all the fields }
example:
curl --request POST '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data ' { "@Name!": "Sam", "age": 30, "state": "CA", "zip": 90008 }'
System.out.println will still print:
ame = Sam, age = 30, state = CA, zip = 90008
Complete customization of deserialization. For example, if you want the state field to take the value CA in both following situations:
{"name":"Sam","age":30,"state":{"postal":"CA"},"zip":90008}
or:
{"name":"Sam","age":30,"state":"CA","zip":90008}
a custom JsonDeserializer can be used:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; public class CustomerDeserializer extends JsonDeserializer<String> { @Override public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { TreeNode tree = jsonParser.readValueAsTree(); String value = tree.toString(); if (value.startsWith("{\"postal\":\"")) { int startPos = "{\"postal\":\"".length(); int endPos = value.lastIndexOf("\"}"); return value.substring(startPos, endPos); } else { return tree.toString().substring(1, tree.toString().length() - 1); } } }
To use the CustomerDeserializer, just annotate the field with it:
public class Customer { private String name; private Integer age; @JsonDeserialize(using = CustomerDeserializer.class) private String state; private Integer zip; // public setter and getter of all the fields }
So both of the following will print the same output:
curl --request POST '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data ' { "name": "Sam", "age": 30, "state": "CA", "zip": 90008 }'
or
curl --request POST '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data ' { "@Name!": "Sam", "age": 30, "state": {"postal":"CA"}, "zip": 90008 }'
Both System.out.println will still print:
name = Sam, age = 30, state = CA, zip = 90008
Fields are dynamic. If the number of fields is not fixed, instead of defining all the fields, define a map and use @JsonAnySetter on the setter method of the map:
Customer class will become:
public class Customer { private Map<String, String> dynamicFields = new HashMap<≶(); @JsonAnySetter public void setDynamicFields(String key, String value) { dynamicFields.put(key, value); } public Map<String, String> getDynamicFields() { return dynamicFields; } }
Change the postCustomer method in the controller for printing differently:
@PostMapping("/customer") public void postCustomer(@RequestBody Customer customer) { for (String s : customer.getDynamicFields().keySet()) { System.out.println("key = " + s + " value = " + customer.getDynamicFields().get(s)); } }
example:
curl --request POST '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data ' { "name": "Sam", "age": 30, "state": "CA", "zip": 90008 }'
System.out.println will print:
key = zip value = 90008
key = name value = Sam
key = state value = CA
key = age value = 30
another example:
curl --request POST '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data ' { "name": "Sam", "state": "CA", "phone": "123456789", "email": "customer@maxjava.net" }'
System.out.println will print:
key = phone value = 123456789
key = name value = Sam
key = state value = CA
key = email value = customer@maxjava.net