Jackson Annotations for JSON (Part 2): Serialization
As we continue our journey through Jackson annotations, let's see what you can use in your POJOs to serialize Java objects to JSON.
Join the DZone community and get the full member experience.
Join For FreeJackson is a suite of data-processing tools for Java comprising of three components:
Streaming (jackson-core) defines low-level streaming APIs and includes JSON-specific implementations.
Annotations (jackson-annotations) contains standard Jackson annotations.
Databind (jackson-databind) implements data-binding (and object serialization) support on the streaming package. This package depends on both the streaming and annotations packages.
In this series of articles, I will explain data binding Java objects to JSON using Jackson annotations. I will take up each of the Jackson annotations and explain, with code snippets, how to use them. Each annotation usage is accompanied with proper test cases.
If you want to catch up on what's happened so far, read:
Serialization Annotations
Jackson provides several annotations that you can use in POJOs to serialize Java objects to JSON. These annotations are:
- @JsonValue
- @JsonInclude
- @JsonGetter
- @JsonAnyGetter
- @JsonPropertyOrder
- @JsonRawValue
- @JsonSerialize
- @JsonRootName
@JsonValue
The @JsonValue annotation is used at the method level. This annotation tells Jackson to use this method to generate the JSON string from the Java object.
Typically, if you want to print a serialized object, you override the toString() method. But, by using the@JsonValue annotation, you can define the way in which the Java object is to be serialized.
Note: Jackson omits any quotation marks inside the String returned by the custom serializer. Let us consider an example Java class that uses the @JsonValue annotation.
ValueDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
public class ValueDemoBean {
@JsonProperty
private long personId = 123L;
@JsonProperty
private String name = "James Clark";
@JsonValue
public String toJson(){
return this.name+","+this.personId+","+this.toString();
}
@Override
public String toString() {
return "ValueDemoBean{" +
"personId=" + personId +
", name='" + name + '\'' +
'}';
}
}
In order to explain the difference between the serialized object with and without the @JsonValue annotation, the code includes the toString() method. You can also run the code without overriding the toString() method.
The test code to test the @JsonValue annotation is:
ValueDemoBeanTest
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;
public class ValueDemoBeanTest {
private ObjectMapper objectMapper;
@Before
public void setUp() throws Exception{
objectMapper = new ObjectMapper();
}
@After
public void tearDown() throws Exception{
objectMapper = null;
}
@Test
public void testSerializingWithJsonValue() throws JsonProcessingException{
String jsonString = objectMapper.writeValueAsString(new ValueDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, containsString("James Clark,123"));
}
}
The output of running the test in IntelliJ is:
As shown in the preceding figure, the Java object is serialized by Jackson by calling the defined methodtoJson(). The quotation marks are added by Jackson.
@JsonInclude
The @JsonInclude annotation is used to exclude properties or fields of a class under certain conditions. This is defined using the JsonInclude.Include enum. This enum contains constants that determine whether or not to exclude the property. The constants are:
- ALWAYS
- NON_DEFAULT
- NON_EMPTY
- NON_NULL
Let us consider an example Java class that uses the @JsonInclude annotation:
IncludeDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class IncludeDemoBean {
public long personId = 123L;
public String name = null;
@Override
public String toString() {
return "IncludeDemoBean{" +
"personId=" + personId +
", name='" + name + '\'' +
'}';
}
}
The test code to test the @JsonInclude annotation is:
@Test
public void testSerializingWithJsonInclude() throws JsonProcessingException{
String jsonString = objectMapper.writeValueAsString(new IncludeDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("123"));
assertThat(jsonString, not(containsString("name")));
}
The output of running the test in IntelliJ is:
As shown in the preceding figure, the JSON string does not contain the property name, as it is initialized to null.
@JsonGetter
The @JsonGetter annotation is used to customize the generated JSON keys. This is accomplished with the value argument of @JsonGetter. The value passed is the name that should be used as the JSON key.
Let us consider an example Java class that uses the @JsonGetter annotation.
GetterDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonGetter;
public class GetterDemoBean {
public long personId = 123L;
public String personName = "James Clark";
@JsonGetter(value = "person-id")
public long getPersonId() {
return personId;
}
@JsonGetter(value = "person-name")
public String getPersonName() {
return personName;
}
}
The test code to test the @JsonGetter annotation is:
@Test
public void testSerializingWithJsonGetter() throws JsonProcessingException {
String jsonString = objectMapper.writeValueAsString(new GetterDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("person-id"));
assertThat(jsonString, containsString("person-name"));
}
The output of running the test in IntelliJ is:
As you can see in the example, the Java object is serialized with the property names that you defined using the@JsonGetter annotation. Without the annotations, the serialized JSON would contain the property names: personId and personName.
@JsonAnyGetter
The @JsonAnyGetter annotation can be used when you don’t want to declare a property or a method for every possible key in JSON. This annotation is used on the getter methods which enables you to use a Map to hold all your properties that you want to serialize.
Let us consider an example Java class that uses the @JsonAnyGetter annotation.
AnyGetterDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import java.util.HashMap;
import java.util.Map;
public class AnyGetterDemoBean {
public long personId = 123L;
public String personName = "James Clark";
private Map<String, String> properties = new HashMap<String, String>();
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
}
The test code to test the @JsonAnyGetter annotation is:
@Test
public void testSerializingWithJsonAnyGetter() throws JsonProcessingException {
AnyGetterDemoBean bean = new AnyGetterDemoBean();
Map<String, String > stringMap = bean.getProperties();
stringMap.put("emailId","james@gmail.com");
stringMap.put("gender","male");
String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean);
System.out.println(jsonString);
assertThat(jsonString, containsString("emailId"));
assertThat(jsonString, containsString("gender"));
}
The output of running the test in IntelliJ is:
As you can see, all the properties are serialized as the properties of the AnyGetterDemoBean object.
@JsonPropertyOrder
The @JsonPropertyOrder annotation tells Jackson to serialize the Java object to JSON in the order specified as the arguments of the annotation. This annotation also allows partial ordering. The properties are first serialized in the order in which they are found, followed by any other properties not included in the annotation.
Let us consider an example of Java class that uses the @JsonPropertyOrder annotation.
PropertyOrderDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"name", "personId"})
public class PropertyOrderDemoBean {
public long personId = 123L;
public String name = "James Clark";
}
The test code to test the @JsonPropertyOrder annotation is:
@Test
public void testSerializingWithJsonPropertyOrder() throws JsonProcessingException {
String jsonString = objectMapper.writeValueAsString(new PropertyOrderDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("123"));
assertThat(jsonString, containsString("James Clark"));
}
The output of running the test in IntelliJ is:
As you can see, the name property is first serialized before the personId. Without the@JsonPropertyOrder annotation, the object would have been serialized in the order found in the class.
@JsonRawValue
The @JsonRawValue annotation is used on methods and fields. It tells Jackson to serialize the field or property as declared. For example, if you have a String field in your Java class, the JSON value that Jackson generates is enclosed within quotes (" "). But when you annotate the field with @JsonRawValue, Jackson omits the quotes.
Let us consider an example Java class that explains the use of @JsonRawValue.
RawValueDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonRawValue;
public class RawValueDemoBean {
public long personId = 0;
public String name = "James Clark";
@JsonRawValue
public String address = "{\"doorNumber\": 1234, \"street\": \"phase-1\", " +
"\"city\": \"New York\"}";
}
Here, the address field is a JSON string. This JSON string will be serialized as a part of the final JSON string of the RawValueDemoBean object.
The test code to test the @JsonRawValue annotation is:
@Test
public void testSerializingWithJsonRawValue() throws JsonProcessingException {
String jsonString = objectMapper.writeValueAsString(new RawValueDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, containsString("{\"doorNumber\": 1234, " +
"\"street\": \"phase-1\", \"city\": \"New York\"}"));
}
The output of running the test in IntelliJ is:
As you can see, the final JSON string of the Java object is generated as defined in the POJO class omitting the quotes.
@JsonSerialize
The @JsonSerialize annotation is used tell Jackson to use the declared custom serializer during the serialization of the field, which is marked with this annotation. Let us consider a POJO that uses the @JsonSerializeannotation.
SerializeDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import guru.springframework.blog.jsonannotation.domain.custom.CustomDateSerializer;
import java.util.Date;
public class SerializeDemoBean {
public long personId = 123L;
public String name = "James Clark";
@JsonSerialize(using = CustomDateSerializer.class)
public Date activeDate;
public void setActiveDate(Date activeDate) {
this.activeDate = activeDate;
}
}
Next, let us define a custom serializer that will serialize the activeDate field with a specific format.
CustomDateSerializer.java
package guru.springframework.blog.jsonannotation.domain.custom;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CustomDateSerializer extends StdSerializer<Date>{
private static SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer(){
this(null);
}
public CustomDateSerializer(Class<Date> t) {
super(t);
}
@Override
public void serialize(Date date, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(simpleDateFormat.format(date));
}
}
The code to test the @JsonSerialize annotation is:
@Test
public void testSerializingWithJsonSerialize() throws JsonProcessingException,ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String date = "29-09-2017 10:00:00";
Date newDate = simpleDateFormat.parse(date);
SerializeDemoBean bean = new SerializeDemoBean();
bean.setActiveDate(newDate);
String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean);
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, containsString("123"));
assertThat(jsonString, containsString("29-09-2017 10:00:00"));
}
The output of running the test in IntelliJ is:
@JsonRootName
The @JsonRootName annotation can be used to tell Jackson to wrap the object to be serialized with a top-level element. You can pass the name as a parameter to the @JsonRootName annotation. Let us consider that you want to wrap your serialized Java object with the key user.
Here is an example of a Java class that uses the @JsonRootName annotation.
RootNameDemoBean.java
package guru.springframework.blog.jsonannotation.domain.serialization;
import com.fasterxml.jackson.annotation.JsonRootName;
@JsonRootName(value = "user")
public class RootNameDemoBean {
public long personId = 0;
public String name = "James Clark";
}
The test code to test the @JsonRootName annotation is:
@Test
public void testSerializingWithJsonRootName() throws JsonProcessingException {
String jsonString = objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE)
.writeValueAsString(new RootNameDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, containsString("user"));
}
The output of running the test in IntelliJ is:
As you can see, the fields personId and name are wrapped within the user, where the latter is the key, and the former is the value of the property of the generated JSON.
Published at DZone with permission of John Thompson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments