Jackson Annotations for JSON (Part 4): General Annotations
In this final series post about Jackson annotations, let's look at some general use annotations that are handy to have at the ready.
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:
General Annotations
The general annotations are:
- @JsonProperty
- @JsonFormat
- @JsonUnwrapped
- @JsonView
- @JsonManagedReference and @JsonBackReference
- @JsonIdentityInfo
- @JsonFilter
@JsonProperty
The @JsonProperty annotation is used to map property names with JSON keys during serialization and deserialization. By default, if you try to serialize a POJO, the generated JSON will have keys mapped to the fields of the POJO. If you want to override this behavior, you can use the @JsonProperty annotation on the fields. It takes a String attribute that specifies the name that should be mapped to the field during serialization.
You can also use this annotation during deserialization when the property names of the JSON and the field names of the Java object do not match.
Let us consider an example Java class that uses the @JsonProperty annotation.
PropertyDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonProperty;
public class PropertyDemoBean {
@JsonProperty("person-id")
public long personId = 123L;
@JsonProperty("name")
public String name = "James Clark";
@Override
public String toString() {
return "PropertyDemoBean{" +
"personId=" + personId +
", name='" + name + '\'' +
'}';
}
}
The test code to test the @JsonProperty annotation is:
@Test
public void testSerializingWithJsonProperty()
throws JsonProcessingException {
String jsonString = objectMapper.writeValueAsString(new PropertyDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, containsString("123"));
}
@Test
public void testDeSerializingWithJsonProperty() throws IOException {
String jsonString = "{\"person-id\": 231, \"name\": \"Mary Parker\"}";
ObjectMapper mapper = new ObjectMapper();
PropertyDemoBean bean = objectMapper.readValue(jsonString, PropertyDemoBean.class);
System.out.println(bean);
assertThat(bean.name, is(equalTo("Mary Parker")));
assertThat(bean.personId, is(equalTo(231 L)));
}
The output of running the test in IntelliJ is:
@JsonFormat
The @JsonFormat annotation is used to tell Jackson that the format in which the value for a field is serialized. It specifies the format using the JsonFormat.Shape enum.
Let us consider an example Java class that uses the @JsonFormat annotation to modify the Date and Time format of an activeDate field.
FormatDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;
public class FormatDemoBean {
@JsonProperty("person-id")
public long personId = 123L;
@JsonProperty("name")
public String name = "James Clark";
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
@JsonProperty("active-date")
public Date activeDate;
public FormatDemoBean() {
}
public void setActiveDate(Date activeDate) {
this.activeDate = activeDate;
}
}
The test code to test the @JsonFormat annotation is:
@Test
public void testSerializingWithJsonFormat()
throws JsonProcessingException, ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
String dateAndTime = "26-09-2017 11:00:00";
Date date = simpleDateFormat.parse(dateAndTime);
FormatDemoBean fb = new FormatDemoBean();
fb.setActiveDate(date);
String jsonString = objectMapper.writeValueAsString(fb);
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, containsString("123"));
assertThat(jsonString, containsString("26-09-2017 11:00:00"));
}
The output of running the test in IntelliJ is:
@JsonUnwrapped
The @JsonUnwrapped annotation unwraps the values during serialization and deserialization. It helps in rendering the values of a composed class as if they belonged to the parent class. Let us consider an example of Java class that uses the @JsonUnwrapped annotation.
UnwrappedDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
public class UnwrappedDemoBean {
public static class Address {
public String doorNumber = "12";
public String streetName = "phase-1";
public String pinCode = "123456";
public String city = "New York";
@Override
public String toString() {
return "Address{" +
"doorNumber='" + doorNumber + '\'' +
", streetName='" + streetName + '\'' +
", pinCode='" + pinCode + '\'' +
", city='" + city + '\'' +
'}';
}
}
public long personId = 0;
public String name = "James Clark";
@JsonUnwrapped
public Address address = new Address();
}
In this example, the Address class is inside the UnwrappedDemoBean class. Without the @JsonUnwrapped annotation, the serialized Java object would be similar to this:
{
"personId": 0,
"name": "James Clark",
"address": {
"doorNumber": "12",
"streetName": "phase-1",
"pinCode": "123456",
"city": "New York"
}
}
Let us see what happens when you use the @JsonUnwrapped annotation.
The test code to test the @JsonUnwrapped annotation is:
@Test
public void testSerializingWithJsonUnwrapped()
throws JsonProcessingException {
String jsonString = objectMapper.writeValueAsString(new UnwrappedDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, not(containsString("address")));
}
The output of running the test in IntelliJ is:
As you can see, the Address object is unwrapped and is displayed as the properties of the parent classUnwrappedDemoBean.
@JsonView
The @JsonView annotation is used to include or exclude a property dynamically during serialization and deserialization. It tells the view in which the properties are rendered. Let us consider an example Java class that uses the @JsonView annotation with Public and Internal views.
ViewDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
public class ViewDemoBean {
@JsonView(Views.Public.class)
@JsonProperty
public long personId = 0;
@JsonView(Views.Public.class)
@JsonProperty
public String name = "James Clark";
@JsonView(Views.Internal.class)
@JsonProperty
public String gender = "male";
@Override
public String toString() {
return "ViewDemoBean{" +
"personId=" + personId +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
The test code to test the @JsonView annotation is:
@Test
public void testSerializingWithJsonView()
throws JsonProcessingException {
String jsonString = objectMapper.writerWithView(Views.Public.class)
.writeValueAsString(new ViewDemoBean());
String jsonStringInternal = objectMapper.writerWithView(Views.Internal.class)
.writeValueAsString(new ViewDemoBean());
System.out.println(jsonString);
System.out.println(jsonStringInternal);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, not(containsString("gender")));
assertThat(jsonStringInternal, containsString("gender"));
}
As you can see in the test code, you need to configure the ObjectMapper to include which type of view must be used for writing the JSON from the Java object using the writerWithView() method.
The output of running the test in IntelliJ is:
When the JSON is generated in the public view, only personId and name fields are serialized omitting the gender field. But when the JSON is generated in the internal view all the fields are serialized.
@JsonManagedReference and @JsonBackReference
The @JsonManagedReference and @JsonBackReference annotation are used to create JSON structures that have a bidirectional relationship. Without this annotation, you get an error like this:
"com.fasterxml.jackson.databind.JsonMappingException:Infinite recursion (StackOverflowError)"
Let us consider an example Java class that uses the @JsonManagedReference and @JsonBackReference annotations:
ManagedReferenceDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonManagedReference;
public class ManagedReferenceDemoBean {
public long personId = 0;
public String name = "James Clark";
@JsonManagedReference
public BackReferenceDemoBean manager;
public ManagedReferenceDemoBean(long personId, String name, BackReferenceDemoBean manager) {
this.personId = personId;
this.name = name;
this.manager = manager;
}
}
BackReferenceDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonBackReference;
import java.util.ArrayList;
import java.util.List;
public class BackReferenceDemoBean {
public long personId = 123;
public String name = "John Thomas";
@JsonBackReference
public List<ManagedReferenceDemoBean> employees;
public BackReferenceDemoBean(long personId, String name) {
this.personId = personId;
this.name = name;
employees = new ArrayList<ManagedReferenceDemoBean>();
}
public void addEmployees(ManagedReferenceDemoBean managedReferenceDemoBean){
employees.add(managedReferenceDemoBean);
}
}
The test code to test both @JsonManagedReference and @JsonBackReference annotations is:
@Test
public void testSerializingWithJsonManagedAndBackReference()
throws JsonProcessingException {
BackReferenceDemoBean demoBean = new BackReferenceDemoBean(123 L, "Mary Parker");
ManagedReferenceDemoBean bean = new ManagedReferenceDemoBean(231 L, "John Thomas", demoBean);
demoBean.addEmployees(bean);
String jsonString = objectMapper.writeValueAsString(bean);
System.out.println(jsonString);
assertThat(jsonString, containsString("John Thomas"));
assertThat(jsonString, containsString("231"));
assertThat(jsonString, not(containsString("employees")));
}
The output of running the test in IntelliJ is:
As you can see, the field marked with @JsonManagedReference is the forward reference which will be included during serialization. The field marked with @JsonBackReference is the back reference and is usually omitted during serialization.
@JsonIdentityInfo
The @JsonIdentityInfo tells Jackson to perform serialization or deserialization using the identity of the object. This annotation works similar to the @JsonManagedReference and @JsonBackReference annotations with the difference that @JsonIdentityInfo includes the back reference object.
Let us consider an example where the IdentityInfoEmployeeDemoBean has a bidirectional relationship withIdentityInfoManagerDemoBean using the @JsonIdentityInfo annotation.
IdentityInfoEmployeeDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "personId")
public class IdentityInfoEmployeeDemoBean {
public long personId = 0;
public String name = "James Clark";
public IdentityInfoManagerDemoBean manager;
public IdentityInfoEmployeeDemoBean(long personId, String name, IdentityInfoManagerDemoBean manager) {
this.personId = personId;
this.name = name;
this.manager = manager;
}
}
IdentityInfoManagerDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import java.util.ArrayList;
import java.util.List;
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "personId")
public class IdentityInfoManagerDemoBean {
public long personId = 123;
public String name = "John Thomas";
public List<IdentityInfoEmployeeDemoBean> employees;
public IdentityInfoManagerDemoBean(long personId, String name) {
this.personId = personId;
this.name = name;
employees = new ArrayList<IdentityInfoEmployeeDemoBean>();
}
public void addEmployees(IdentityInfoEmployeeDemoBean identityInfoEmployeeDemoBean){
employees.add(identityInfoEmployeeDemoBean);
}
}
The test code to test the @JsonIdentityInfo annotation is:
@Test
public void testSerializingWithJsonIdentityInfo()
throws JsonProcessingException {
IdentityInfoManagerDemoBean demoBean = new IdentityInfoManagerDemoBean(123 L, "Mary Parker");
IdentityInfoEmployeeDemoBean bean = new IdentityInfoEmployeeDemoBean(231 L, "John Thomas", demoBean);
demoBean.addEmployees(bean);
String jsonString = objectMapper.writeValueAsString(bean);
System.out.println(jsonString);
assertThat(jsonString, containsString("John Thomas"));
assertThat(jsonString, containsString("231"));
assertThat(jsonString, containsString("employees"));
}
The output of running the test in IntelliJ is:
As you can see, the output gives the information about the employee with his manager details. It also provides the additional information about the employees under the manager.
@JsonFilter
The @JsonFilter annotation is used to tell Jackson to use a custom defined filter to serialize the Java object. To define your filter, you need to use the FilterProvider class. This provider gets the actual filter instance to use. The filter is then configured by assigning the FilterProvider to ObjectMapper.
Let us consider an example of Java class that uses the @JsonFilter annotation.
FilterDemoBean.java
package guru.springframework.blog.jsonannotation.domain.general;
import com.fasterxml.jackson.annotation.JsonFilter;
@JsonFilter("customFilter")
public class FilterDemoBean {
public long personId = 123L;
public String name = "James Clark";
public String gender = "male";
}
The test code to test the @JsonFilter annotation is:
@Test
public void testSerializingWithJsonFilter()
throws JsonProcessingException {
FilterProvider filterProvider = new SimpleFilterProvider().
addFilter("customFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String jsonString = objectMapper.writer(filterProvider).
writeValueAsString(new FilterDemoBean());
System.out.println(jsonString);
assertThat(jsonString, containsString("James Clark"));
assertThat(jsonString, not(containsString("123")));
}
The output of running the test in IntelliJ is:
As you can see, the custom filter declared as the arguments of the @JsonFilter annotation extract only the name and filters out the other properties of the bean during serialization.
You can download the source code of this post from here.
Published at DZone with permission of John Thompson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments