JSON Tutorial – Jackson Annotations – Part 1
JSON Tutorial – Jackson Annotations – Part 1
In this post we will discuss how to Jackson Annotations are used and the affects they have on the serialization and deserialization of JSON / Java Objects. This tutorial will give you a better understanding of Jackson Annotations and how they are best used.
What’s Covered
Property Specific
Deserialization and Serialization details
@JsonProperty
The Jackson Annotation @JsonProperty is used on a property or method during serialization or deserialization of JSON. It takes an optional string parameter which allows you to override the property name in JSON.
For example, without the @JsonProperty annotations my JSON properties would all be display as camel case instead of the hyphen separated names that I am proposing.
public class User { public int id; @JsonProperty("first-name") public String firstName; @JsonProperty("last-name") public String lastName; private Address address; @JsonProperty("mobile-phone") private Phone mobilePhone; @JsonProperty("home-phone") private Phone homePhone; @JsonProperty("work-phone") protected Phone workPhone; }
With @JsonProperty Annotations
In this example, you will notice that all properties that had the @JsonProperty annotation will now contain the override property name values. Please note: the Phone class was not modified and as a result phoneType, phoneNumber and phoneSystemIdentifier are still displayed in camelCase.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "home-phone" : null, "work-phone" : null }
Without @JsonProperty Annotations
{ "id" : 1, "firstName" : "Amaury", "lastName" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobilePhone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "homePhone" : null, "workPhone" : null }
@JsonAutoDetect
The Jackson Annotation @JsonAutoDetect is used to change the default process of introspection used by Jackson and modify the set of properties which will get serialized. We can define the visibility on the following elements: creatorVisibility, fieldVisibility, getterVisibility, isGetterVisibility, and setterVisibility.
The JsonAutoDetect class has defined public static constants mimicking the Java Class visibility levels: ANY, DEFAULT, NON_PRIVATE, NONE, PROTECTED_AND_PUBLIC and PUBLIC_ONLY.
For this example, you will notice that I have defined differing access levels for various member fields in my User Class. Now we’ll change the fieldVisibility and see what happens in the serialized JSON.
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NON_PRIVATE) @JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { public int id; @JsonProperty("first-name") public String firstName; @JsonProperty("last-name") public String lastName; private Address address; private Phone mobilePhone; private Phone homePhone; protected Phone workPhone; }
JSON @JsonAutoDetect with NON_PRIVATE
In this example, we will only display non-private class member fields. So any public, protected or default member fields will be included.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "workPhone" : null }
JSON @JsonAutoDetect with PUBLIC_ONLY
By setting the visibility level to public only with @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY) Jackson will only display those member fields whose access modifier is public.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes" }
JSON @JsonAutoDetect with ANY
By setting the visibility level to ANY with @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) Jackson will accept any access modifier (private, protected, default or public) and show all available member fields.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobilePhone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "homePhone" : null, "workPhone" : null }
@JsonIgnore
The Jackson Annotation @JsonIgnore is used to prevent certain properties from the being included in the serialization / deserialization process. In this example, you will notice how certain fields will be excluded from the JSON output below.
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) @JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; private Address address; @JsonProperty("mobile-phone") private Phone mobilePhone; @JsonIgnore private Phone homePhone; @JsonProperty("work-phone") private Phone workPhone; @JsonIgnore private String accountName; @JsonIgnore private String accessIdentifier; }
Before @JsonIgnore
Prior to adding the @JsonIgnore annotation, our JSON contains all of the fields as the fieldVisibility has been set to JsonAutoDetect.Visibility.ANY, causing all fields to be used irrespective of their access modifiers. As you can see, our homeNumber (if there was one), our accountName and accountIdentifier are being displayed in the serialized JSON.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "homePhone" : null, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "work-phone" : null, "account-name" : "av50333", "account-identifier" : "AC652-87230F-128BE9-1897DA" }
After @JsonIgnore
Once we have added the Jackson Annotation @JsonIgnore to those specific fields, you will see that the serialized JSON output no longer contains those fields. These fields would be ignored in both the serialization (Writing) of Java Object to JSON and the deserialization (Reading) of JSON to Java Objects.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "work-phone" : null }
@JsonIgnoreProperties
The Jackson Annotation @JsonIgnoreProperties defined prior to the class level declaration is used to prevent multiple properties from the being included in the serialization / deserialization process. In this example, you will notice how certain fields will be excluded from the JSON output below.
@JsonIgnoreProperties({"home-phone", "account-name", "account-identifier"}) @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) @JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; private Address address; @JsonProperty("mobile-phone") private Phone mobilePhone; @JsonProperty("home-phone") private Phone homePhone; @JsonProperty("work-phone") private Phone workPhone; @JsonProperty("account-name") private String accountName; @JsonProperty("account-identifier") private String accessIdentifier; }
Before @JsonIgnoreProperties
This example, follows the same paradigm as our previous case with @JsonIgnore. Prior to adding the @JsonIgnoreProperties annotation, our JSON contains all of the fields as the fieldVisibility has been set to JsonAutoDetect.Visibility.ANY, causing all fields to be used irrespective of their access modifiers. As you can see, our homeNumber (if there was one), our accountName and accountIdentifier are being displayed in the serialized JSON.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "home-phone" : null, "work-phone" : null, "account-name" : "av50333", "account-identifier" : "AC652-87230F-128BE9-1897DA" }
After @JsonIgnoreProperties
After we added the class level annotation of @JsonIgnoreProperties with the three properties we were to ignore ({“home-phone“, “account-name“, “account-identifier“}) you will note how they have been suppressed in the JSON output.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "work-phone" : null }
@JsonInclude
The Jackson Annotation @JsonInclude is used to include property under certain conditions as determined by the JsonInclude.Include Enum. The Enum contains the following four constants: ALWAYS, NON_DEFAULT, NON_EMPTY, and NON_NULL.
Sample Code Used for @JsonInclude
Address address = new Address(); address.setAddress1("100 Main Street"); address.setCity("Eureka"); address.setState("Montana"); address.setZipCode("59917"); Phone mobilePhone = new Phone(); mobilePhone.setPhoneNumber("800-222-5454"); mobilePhone.setPhoneType("Apple iPhone6"); mobilePhone.setPhoneSystemIdentifier(Provider.VERIZON); User user1 = new User(); user1.setId(1); user1.setFirstName("Amaury"); user1.setLastName("Valdes"); user1.setAddress(address); user1.setMobilePhone(mobilePhone); user1.setAccountName(""); user1.setAccessIdentifier("AC652-87230F-128BE9-1897DA");
User Class with JSON Annotations
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) @JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; private Address address; @JsonInclude(JsonInclude.Include.NON_NULL) @JsonProperty("mobile-phone") private Phone mobilePhone; @JsonInclude(JsonInclude.Include.NON_NULL) @JsonProperty("home-phone") private Phone homePhone; @JsonInclude(JsonInclude.Include.NON_NULL) @JsonProperty("work-phone") private Phone workPhone; @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonProperty("account-name") private String accountName; @JsonProperty("account-identifier") private String accessIdentifier; }
Without @JsonInclude
Without the @JsonInclude annotation, we can see that our JSON contains both null values and empty values. This might be fine in many instances but when you would much rather only output properties that contain values or that contain values modified from the original default value (NON_DEFAULT).
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "home-phone" : null, "work-phone" : null, "account-name" : "", "account-identifier" : "AC652-87230F-128BE9-1897DA" }
With @JsonInclude
Once we used @JsonInclude annotation, we can see that our JSON contains only non-null and non-empty property values.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "mobile-phone" : { "phoneType" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, "account-identifier" : "AC652-87230F-128BE9-1897DA" }
@JsonFormat
The Jackson Annotation @JsonFormat is used to define how values of properties are to be serialized. @JsonFormat is used to modify the shape as determined by the JsonFormat.Shape Enum. The Enum contains the following constants: ANY, ARRAY, BOOLEAN, NUMBER, NUMBER_FLOAT, NUMBER_INT, OBJECT, SCALAR, and STRING.
In this example, we use @JsonFormat with a SimpleDateFormat-compatible pattern definition of MM/dd/yyyy HH:mm:ss to format the activeDate field into the data and time components.
CAUTION
According to the Jackson Documentation, it is recommended that you NEVER use ‘java.sql.Date’ as there are known issues with respect to timezone handling, partly due to design of this class.
@JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; private Address address; @JsonProperty("mobile-phone") private Phone mobilePhone; @JsonProperty("home-phone") private Phone homePhone; @JsonProperty("work-phone") private Phone workPhone; @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM/dd/yyyy HH:mm:ss") @JsonProperty("active-date") private Date activeDate; }
Without @JsonFormat
As you can see from the example below, when dates are serialized by Jackson into JSON they will, by default, use the most efficient and accurate representation available. To this end the representation used is epoch timestamp (number of milliseconds since January 1st, 1970, UTC). In Java this is equivalent to using the System.currentTimeMillis().
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "active-date" : 1422894840399, "mobile-phone" : { "phoneType" : "MOBILE", "phoneModel" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" } }
Use @JsonFormat to Format Date/Time in JSON
Once we use @JsonFormat you will notice how the active-date property gets set properly to the Date/Time specified in the pattern.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "active-date" : "02/02/2015 16:32:09", "mobile-phone" : { "phoneType" : "MOBILE", "phoneModel" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" } }
@JsonUnwrapped
The Jackson Annotation @JsonUnwrapped allows for the inlining or unwrapping of child objects into the main object. In my User example, you will notice that I have the >Address> class inside of the main class. By using @JsonUnwrapped the properties of the child object as if they are properties of the parent class instead.
@JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; @JsonUnwrapped private Address address; @JsonProperty("account-name") private String accountName; @JsonProperty("account-identifier") private String accessIdentifier; private ArrayList<Phone> phoneList = new ArrayList<Phone>(); @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM/dd/yyyy HH:mm:ss") @JsonProperty("active-date") private Date activeDate; }
Without @JsonUnwrapped
Without using the @JsonUnwrapped annotation, you will notice that our Address class gets serialized as follows:
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "account-name" : "av50333", "account-identifier" : "AC652-87230F-128BE9-1897DA", "active-date" : "02/02/2015 17:08:37" }
With @JsonUnwrapped
Once we add the @JsonUnwrapped annotation, the child object (Address) is unwrapped and its child properties will appear to be part to the parent class, which in our case is the User class.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917", "account-name" : "av50333", "account-identifier" : "AC652-87230F-128BE9-1897DA", "active-date" : "02/02/2015 17:08:38" }
With @JsonUnwrapped Using Prefix, Suffix or Both
The Jackson Annotation @JsonUnwrapped also allows us to pass in optional parameters for prefix and suffix. This optional parameter will allow us to prepend or append prefix and suffix information to all of the properties in the unwrapped object.
@JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; @JsonUnwrapped(prefix="address.", suffix=".user") private Address address; @JsonProperty("account-name") private String accountName; @JsonProperty("account-identifier") private String accessIdentifier; private ArrayList<Phone> phoneList = new ArrayList<Phone>(); @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM/dd/yyyy HH:mm:ss") @JsonProperty("active-date") private Date activeDate; }
Result of Prefix and Suffix using @JsonUnwrapped
Using these optional parameters we can create the following JSON serialized output.
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address.address1.user" : "100 Main Street", "address.address2.user" : null, "address.city.user" : "Eureka", "address.state.user" : "Montana", "address.zipCode.user" : "59917", "account-name" : "av50333", "account-identifier" : "AC652-87230F-128BE9-1897DA", "active-date" : "02/02/2015 17:08:38" }
@JsonView
The Jackson Annotation @JsonView defines which properties will be included for serialization or deserialization of JSON at runtime. JSON views work differently than other annotations which are applied statically to properties. By using JSON views we are able to dynamically define which properties will be included or excluded from the serialization process. One good example, would be to have a public view which would include a smaller subset of properties and a private or internal view which includes the larger set of properties.
Defining the Views Class
package com.avaldes.model; public class Views { public static class Public { } public static class Internal extends PublicView { } }
Using @JsonView on User Class
@JsonPropertyOrder({"id", "first-name", "last-name"}) public class User { private int id; @JsonProperty("first-name") private String firstName; @JsonProperty("last-name") private String lastName; private Address address; @JsonProperty("account-name") private String accountName; @JsonProperty("account-identifier") private String accessIdentifier; private ArrayList<Phone> phoneList = new ArrayList<Phone>(); @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM/dd/yyyy HH:mm:ss") @JsonProperty("active-date") private Date activeDate; @JsonView(Views.Public.class) public Address getAddress() { return address; } @JsonView(Views.Internal.class) public ArrayList<Phone> getPhones() { return phoneList; } @JsonView(Views.Internal.class) public String getAccountName() { return accountName; } }
@JsonView with Public View
By using @JsonView annotation with Public View you will notice that a smaller subset of properties are displayed as compared to Internal View.
Using ObjectMapper.writerWithView for Public Views Class
try { json = mapper.writerWithView(Views.Public.class) .withDefaultPrettyPrinter().writeValueAsString(user1); System.out.println(json); } catch (IOException e) { e.printStackTrace(); }
Public View Output
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "address" : { "address1" : "100 Main Street", "address2" : null, "city" : "Eureka", "state" : "Montana", "zipCode" : "59917" }, "active-date" : "03/06/2016 02:44:37" }
@JsonView with Internal View
@JsonView annotation with Internal View, you will notice that some additional fields will get serialized as well.
Using ObjectMapper.writerWithView for Internal Views Class
try { json = mapper.writerWithView(Views.Internal.class) .withDefaultPrettyPrinter().writeValueAsString(user1); System.out.println(json); } catch (IOException e) { e.printStackTrace(); }
Internal View Output
{ "id" : 1, "first-name" : "Amaury", "last-name" : "Valdes", "phones" : [ { "phoneType" : "MOBILE", "phoneModel" : "Apple iPhone6", "phoneNumber" : "800-222-5454", "phoneSystemIdentifier" : "VERIZON" }, { "phoneType" : "WORK", "phoneModel" : "Standard", "phoneNumber" : "800-234-9999", "phoneSystemIdentifier" : "VoIP" } ], "account-name" : "av50333", "account-identifier" : "AC652-87230F-128BE9-1897DA", "active-date" : "03/06/2016 03:07:48" }

Please Share Us on Social Media






Leave a Reply