Converting Between XML and JSON Using JAXB and Jackson

JAXB converts XML to Java and Jackson handles the Java to JSON Conversion

“You only live once, but if you do it right, once is enough.” ― Mae West

1. Introduction

In a previous article we covered the basics of using JAXB with its annotations to convert java objects to XML and back. In this article, we take a look at converting from XML to JSON and back, using jackson for the JSON conversion.

Continue reading “Converting Between XML and JSON Using JAXB and Jackson”

How to Convert Large CSV to JSON

Learn how to use Jackson Streaming to convert a large CSV to JSON.

“We are a way for the cosmos to know itself.”
― Carl Sagan, Cosmos

1. Introduction

Let us today look into converting a large CSV to JSON without running into memory issues. This previous article showed how to parse CSV and output the data to JSON using Jackson. However, since that code loads the entire data into memory, it will run into issues loading large CSV files such as:

Continue reading “How to Convert Large CSV to JSON”

How to Convert CSV to JSON in Java

Convert CSV to JSON using Jackson. Use a POJO for conversion or a List & Map for intermediate storage.

“Any fool can know. The point is to understand.”
― Albert Einstein

 

1. Introduction

CSV to JSON conversion is easy. In this article, we present a couple of methods to parse CSV data and convert it to JSON. The first method defines a POJO and uses simple string splitting to convert CSV data to POJO, which in turn is serialized to JSON. The second method uses a more complete CSV parser with support for quoted fields and commas embedded within fields. In this method, we use the Java Collection classes to store the parsed data and convert those to JSON.

Continue reading “How to Convert CSV to JSON in Java”

Jackson Tree Model

1. Introduction

Jackson represents the JSON object model as a tree of JsonNode objects. This is called the Tree Model since it comprises of a tree of JsonNodes, including ObjectNode and ArrayNode. The tree mirrors the structure of the JSON document. It can be created by parsing a JSON document or by creating an in-memory representation.

2. Parsing a JSON File

A JSON file can be parsed and a tree model representation can be created using the ObjectMapper.readTree().

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File(args[0]));

The resulting tree model can be converted back to a JSON document as follows:

System.out.println(mapper.writeValueAsString(root));

3. Convert a Java Object (POJO) to JsonNode

Let us learn how to build a tree model from a POJO (Plain Old Java Object). Consider these classes:

public class Address {
    private String address1;
    private String address2;
    private String city;
    private String state;
    private String zip;
}

public class User
{
    private String firstName;
    private String lastName;
    private Address address;
}

Instantiate the objects as required:

User user = new User();
user.setFirstName("Harrison");
user.setLastName("Ford");
Address address = new Address();
address.setAddress1("123 Main Street");
address.setCity("Hollywood");
address.setState("CA");
address.setZip("33023");
user.setAddress(address);

A single function call suffices to convert the POJO to a JsonNode.

JsonNode root = mapper.valueToTree(user);
System.out.println(mapper.writeValueAsString(root));

The output JSON is:

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "address" : {
    "address1" : "123 Main Street",
    "address2" : null,
    "city" : "Hollywood",
    "state" : "CA",
    "zip" : "33023"
  }
}

4. Convert a JsonNode to POJO

Once you have assembled a tree model from the JsonNode instances, how can you convert it to a POJO? Quite easy and all the contained JsonNode instances are also converted properly.

User user = mapper.treeToValue(root, User.class);

5. Convert Generic Containers to JsonNode

You can build up an object representation from nothing more than Java’s generic containers such as Map and List. Is it possible to convert such a representation to a tree model (JsonNode)? Of course it is. Use ObjectMapper.valueToTree() as before.

Map<String, Object> user = new HashMap<>();
user.put("firstName", "Harrison");
user.put("lastName", "Ford");
user.put("emailAddress", Arrays.asList("harrison@example.com",
              "hford@actors.com"));

Map<String, Object> addr = new HashMap<>();
addr.put("address1", "123 Main Street");
addr.put("address2", null);
addr.put("city", "Hollywood");
addr.put("state", "CA");
addr.put("zip", "33023");
user.put("address", addr);

JsonNode root = mapper.valueToTree(user);
System.out.println(mapper.writeValueAsString(root));

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "emailAddress" : [ "harrison@example.com", "hford@actors.com" ],
  "address" : {
    "zip" : "33023",
    "address2" : null,
    "city" : "Hollywood",
    "address1" : "123 Main Street",
    "state" : "CA"
  }
}

And of course, once you have the JsonNode tree model, you can use Jackson data binding to create a Java object (POJO).

User userAgain = mapper.treeToValue(root, User.class);

6. Iterating over a JsonNode

Once you have a JsonNode, how can you find out what it contains? You can loop over its contents and extract the children.

Find the key names of a JsonNode (assuming it is an ObjectNode). And lookup the value JsonNode using JsonNode.get(String).

JsonNode parent = ...;
for (Iterator<String> it = parent.fieldNames() ; it.hasNext() ; ) {
    String field = it.next();
    System.out.println(field + " => " + parent.get(field));
}

Or iterate over the fields, again assuming it is an ObjectNode:

for (Iterator<Map.Entry<String,JsonNode>> it = parent.fields() ;
     it.hasNext() ; ) {
    Map.Entry<String,JsonNode> e = it.next();
    System.out.println(e.getKey() + " => " + e.getValue());
}

Fetch and iterate over all the elements. This one works with an ArrayNode too.

for (Iterator<JsonNode> it = parent.elements() ; it.hasNext() ; ) {
    JsonNode node = it.next();
    System.out.println(node);
}

7. Creating a Tree Model from Scratch

You can also completely generate a tree model from scratch. Start by creating an ObjectNode and populate it using JsonNode.with(). Here we create the above User instance with the associated Address as shown below.

ObjectNode root = mapper.createObjectNode();
root.put("firstName", "Harrison");
root.put("lastName", "Ford");
root.with("address").put("address1", "123 Main Street");
root.with("address").put("address2", null);
root.with("address").put("city", "Hollywood");
root.with("address").put("state", "CA");
root.with("address").put("zip", "33023");
System.out.println(mapper.writeValueAsString(root));

8. Adding Items to a List

For adding lists into the JSON tree model, you can start with the method withArray() and add items as shown:

root.withArray("Genre").add("Drama").add("Horror");

// results in:
{
 ..
 "Genre" : [ "Drama", "Horror" ],
 ..
}

9. Adding from a List

Here is an example of using Java 8 Streams to add nodes from a List:

Arrays.asList("Stephen King (novel)",
                 "Stanley Kubrick (screenplay)",
                 "Diane Johnson (screenplay)")
    .stream()
    .forEach(root.withArray("Writer")::add);

// looks like:
{
 ..
 "Writer" : [ "Stephen King (novel)", "Stanley Kubrick (screenplay)", "Diane Johnson (screenplay)" ]
 ..
}

10. Adding from a Map

And adding to a tree model from a Map is similarly easy:

map.put("cat", "meow");
map.put("dog", "bark");
map.put("cow", "moo");
map
    .entrySet()
    .stream()
    .forEach(e -> root.with("animals").put(e.getKey(), e.getValue()));

// output:
{
 "animals" : {
   "cat" : "meow",
   "cow" : "moo",
   "dog" : "bark"
 }
}

11. Using JsonPointer

JsonPointer is a proposed standard for addressing nodes within a JSON document (somewhat similar to XPath for XML documents). Jackson supports extraction of JsonNodes with a JsonPointer expression using the method JsonNode.at().

JsonNode root = mapper.readTree(new File(jsonFile));
String jsonPtr = ...;
System.out.println(mapper.writeValueAsString(root.at(jsonPtr)));

Let us see some examples of JsonPointer. Consider this JSON.

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "emailAddress" : [ "harrison@example.com", "hford@actors.com" ],
  "address" : {
    "zip" : "33023",
    "address2" : null,
    "city" : "Hollywood",
    "address1" : "123 Main Street",
    "state" : "CA"
  }
}

Here are some examples of JsonPointer expression evaluation on this document.

/firstName: "Harrison"

/emailAddress: [ "harrison@example.com", "hford@actors.com" ]

/emailAddress/0: "harrison@example.com"

/emailAddress/1: "hford@actors.com"

/address: {
  "zip" : "33023",
  "address2" : null,
  "city" : "Hollywood",
  "address1" : "123 Main Street",
  "state" : "CA"
}

/address/zip: "33023"

Summary

This article demonstrated how to easily work with the tree model in JSON. We showed how to parse JSON into JsonNode and also how to extract information from the tree model.

Guide to Jackson Annotations

1. Introduction

Jackson provides a number of annotations which help tweak various facets of JSON serialization and deserialization. In this guide, we present some important and commonly used Jackson annotations.

2. @JsonProperty

The annotation @JsonProperty is used to change the JSON property name used in serialization and deserialization. For example,

public class User {
    @JsonProperty("FirstName")
    private String firstName;
}

Output before and after:

// before
...
  "firstName" : "Harrison",
...

// after
  "FirstName" : "Harrison",
...

2.1. Confusion with required

The annotation @JsonProperty defines an attribute required which does not quite work the way it is expected. Despite being specified, it does not mark the property as required either during serialization or deserialization. It applies only when using @JsonProperty with @JsonCreator. For example, the following does not require the presence of the the property “FirstName“:

public class User {
    @JsonProperty(value="LastName", required=true)
    private String lastName;
}

2.2. @JsonProperty and @JsonCreator

@JsonProperty can be used with @JsonCreator to indicate the constructor to use when deserializing JSON. Consider the following class which does not implement a no-arguments default constructor but has a constructor taking appropriate arguments.

public class MyClass {
    private String firstName;
    private String lastName;

    public MyClass(String firstName,String lastName)
    {
	this.firstName = firstName;
	this.lastName = lastName;
    }
}

Create an instance of this class and serialize it to JSON.

MyClass obj = new MyClass("Harrison", "Ford");
System.out.println(mapper.writeValueAsString(obj));

// serialized to:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
}

Deserializing this JSON results in a JsonMappingException. That is because Jackson cannot create an instance of the class in the absence of a default constructor.

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of sample.sample11$MyClass: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

What if you do not want to add a default constructor due to design considerations? Jackson offers a solution: use @JsonCreator with @JsonProperty to properly mark the constructor. The constructor takes two arguments, both of which are marked required. With this change, Jackson is able to create an instance of MyClass with the proper arguments.

public class MyClass {
    private String firstName;
    private String lastName;
    private Address address;

    @JsonCreator
    public MyClass(@JsonProperty(value = "firstName",required = true) String firstName,
		   @JsonProperty(value = "lastName", required = true) String lastName)
    {
	this.firstName = firstName;
	this.lastName = lastName;
    }
}

3. @JsonIgnore

Property-level annotation used to ignore specified properties. These properties are ignored during both serialization and deserialization. For instance, the following property password is neither written to JSON nor read from JSON.

...
    @JsonIgnore
    private String password;
...

4. @JsonIgnoreProperties

Class level annotation used to ignore multiple properties.

@JsonIgnoreProperties({"bar", "baz"})
public class MyClass
{
    private String foo;
    private String bar;
    private Integer baz;
    ...
}

Additionally, you can use this annotation to ignore unknown properties during deserialization. If unknown properties are present in the JSON, you will get an UnrecognizedPropertyException.

@JsonIgnoreProperties(ignoreUnknown=true, {"bar", "baz"})
public class MyClass
{
    private String foo;
    private String bar;
    ...
}

Parsing the following JSON will not result in an exception due to specifying ignoreUnknown.

{
  "foo" : "value of foo",
  "bar" : "value of bar",
  "qux" : "value of qux"
}

5. @JsonIgnoreType

To ignore a particular class (or built-in type) during serialization and deserialization, use the @JsonIgnoreType annotation on the class. In the following, the MyClass.address property will not be serialized or deserialized.

@JsonIgnoreType
public class Address {
    private String address1;
    ...
}

public class MyClass {
    private Address address;
    private String firstName;
    private String lastName;
}

Note that MyClass.address property has bot been serialized. It will be ignored during deserialization.

{
  "firstName" : "Harrison",
  "lastName" : "Ford"
}

6. @JsonFormat

@JsonFormat is used to specify some extra details when serializing certain types especially dates.

The date field in the following class is serialized by default as a timestamp.

public class MyClass
{
    private Date createdDate = new Date();
}

// serialized to:
{
  "createdDate" : 1485913763410
}

Annotate the date field with @JsonFormat and use pattern to set the date format:

public class MyClass
{
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ")
    private Date createdDate;
}

Serialization now shows the date in the specified format:

{
  "createdDate" : "2017-02-01@01:58:28.130+0000"
}

Deserialization also works as expected. If the date is not specified in the same format as required by the pattern, you get the following exception:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "...": expected format "yyyy-MM-dd@HH:mm:ss.SSSZ" ...

7. @JsonUnwrapped

You can use @JsonUnwrapped to indicate to Jackson that a contained object should be unwrapped during serialization i.e. the contained object properties should be included among the properties of the parent. An example:

public class Address {
    private String address1;
    private String address2;
    private String city;
    private String state;
    private String zip;
}

public class MyClass {
    private String firstName;
    private String lastName;
    @JsonUnwrapped
    private Address address;
}

When an object of MyClass is serialized, the properties of address are included among the properties of MyClass:

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "address1" : "123 Main Street",
  "address2" : null,
  "city" : "Hollywood",
  "state" : "CA",
  "zip" : "33023"
}

To prevent clashes between the parent and child object properties, a prefix or suffix can be specified to be added to the child properties:

public class MyClass {
    private String firstName;
    private String lastName;
    @JsonUnwrapped(prefix="addr.")
    private Address address;
}

The resulting JSON is now:

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "addr.address1" : "123 Main Street",
  "addr.address2" : null,
  "addr.city" : "Hollywood",
  "addr.state" : "CA",
  "addr.zip" : "33023"
}

During deserialization, either of the forms shown are accepted without errors.

// normal form
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "address" : {
    "address1" : "123 Main Street",
    "address2" : null,
    "city" : "Hollywood",
    "state" : "CA",
    "zip" : "33023"
  }
}

// no prefix
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "address1" : "123 Main Street",
  "address2" : null,
  "city" : "Hollywood",
  "state" : "CA",
  "zip" : "33023"
}

// with prefix
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "addr.address1" : "123 Main Street",
  "addr.address2" : null,
  "addr.city" : "Hollywood",
  "addr.state" : "CA",
  "addr.zip" : "33023"
}

8. @JsonGetter

This annotation can be used on a no-arguments method returning non-void, with the value specified to indicate the name of a logical property. An example, serializing an object of the class below will include a property “joe” defined as the value returned from the method theDate().

public class MyClass {
    private String firstName;
    private String lastName;

    @JsonGetter("joe")
    public Date theDate()
    {
	return new Date();
    }
}

// serialized to:
{
 "firstName" : "Harrison",
 "lastName" : "Ford",
 "joe" : 1485931403261
}

9. @JsonAnyGetter and @JsonAnySetter

In some instances, you may have a situation where a class has dynamic properties stored in a Map and these properties need to be exposed to JSON. In this situation, the getter method or the Map field can be marked with the @JsonAnyGetter annotation. An example will illustrate the point.

public class MyClass {
    private String firstName;
    private String lastName;
    private Address address;
    private Map<String,Object> moreProps = new HashMap<>();
}

// set properties
MyClass obj = new MyClass();
...
obj.setAddress(address);
obj.getMoreProps().put("born", "Chicago, Illinois");
System.out.println(mapper.writeValueAsString(obj));

// serialized to:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "moreProps" : {
    "born" : "Chicago, Illinois"
  }
}

As expected, the Map field appears as a separate object within the container object. Using the @JsonAnyGetter merges the Map entries into the parent JSON.

public class MyClass {
    private String firstName;
    private String lastName;
    private Address address;
    private Map<String,Object> moreProps = new HashMap<>();

    @JsonAnyGetter
    public Map<String,Object> getMoreProps() {
        return moreProps;
    }
}

// create and set object properties here as before
MyClass obj = new MyClass();
...

// serialized to:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "born" : "Chicago, Illinois"
}

That takes care of serialization. How about deserialization? JSON serialized for the above case cannot directly be used for deserialization as it results in an UnrecognizedPropertyException.

The solution is to use @JsonAnySetter on a method which accepts a key and a value to set the property. See below.

public class MyClass {
    ... // as before

    @JsonAnySetter
    public void setMore(String key,Object value)
    {
	moreProps.put(key, value);
    }
}

With this method definition and annotation in place, deserialization is also a breeze.

10. @JsonValue

@JsonValue can be used to serialize the complete state of an object using a single method rather than the usual serialization using properties. An example will help clarify the picture.

The following class marks the toString() method with @JsonValue. When serializing this object, Jackson uses the representation returned from this method as the value of the object. Note that the value returned is not valid JSON.

public class Address {
    private String address1;
    private String address2;
    private String city;
    private String state;
    private String zip;

    @JsonValue
    public String toString()
    {
	StringBuilder sbuf = new StringBuilder();
	sbuf.append(address1).append("|")
	    .append(address2).append("|")
	    .append(city).append("|")
	    .append(state).append("|")
	    .append(zip).append("|");
	return sbuf.toString();
    }
}

Serializing an instance of the above class yields:

"123 Main Street|null|Hollywood|CA|33023|"

Let us see how this works when included as a part of another class.

public class MyClass {
    private String firstName;
    private String lastName;
    private Address address;
}

After suitable initialization and serialization, we have the following JSON output. Note that the method annotated with @JsonValue was used for converting the value of Address to its representation.

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "address" : "123 Main Street|null|Hollywood|CA|33023|"
}

10.1. @JsonValue and @JsonCreator

Now, how do we deserialize this JSON into the proper object? We make use of the @JsonCreator annotation to mark a static factory method (or a constructor) which can create the object from its string representation. This method does the opposite of what the @JsonValue method does: it splits the string representation and sets the appropriate fields.

public class Address {
    private String address1;
    private String address2;
    ...

    @JsonCreator
    static public Address fromString(String value)
    {
	String[] values = value.split("\\|");
	Address obj = new Address();
	int index = 0;
	if ( index < values.length ) {
	    obj.setAddress1(values[index]);
	    index++;
	}
	if ( index < values.length ) {
	    obj.setAddress2(values[index]);
	    index++;
	}
	if ( index < values.length ) {
	    obj.setCity(values[index]);
	    index++;
	}
	if ( index < values.length ) {
	    obj.setState(values[index]);
	    index++;
	}
	if ( index < values.length ) {
	    obj.setZip(values[index]);
	    index++;
	}
	return obj;
    }
}

11. @JsonPropertyOrder

Sometimes you may need to specify the order in which the properties are serialized. By default properties are serialized in the order they are defined in the class. If this order is not satisfactory, you can use @JsonPropertyOrder to specify the order of appearance.

The following definition illustrates the point. Note the order in which the properties are serialized in JSON.

public class MyClass {
    private Address address;
    private String firstName;
    private String lastName;
}

// serialized by default as:
{
  "address" : {
    "address1" : "123 Main Street",
    "address2" : null,
    "city" : "Hollywood",
    "state" : "CA",
    "zip" : "33023"
  },
  "firstName" : "Harrison",
  "lastName" : "Ford"
}

Now applying the @JsonPropertyOrder annotation as shown rearranges the order to the required.

@JsonPropertyOrder(value = {"firstName", "lastName", "address"})
static public class MyClass {
    private Address address;
    ...
}

// serialized to:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "address" : {
    "address1" : "123 Main Street",
    "address2" : null,
    "city" : "Hollywood",
    "state" : "CA",
    "zip" : "33023"
  }
}

12. Mixin Annotations

Sometimes the class being used in JSON conversion may not be available for direct modification to add or remove annotations. Maybe the class is a part of a library and hence cannot be edited. How can we declare the annotations that we want on such classes? We use Mixins! Mixin classes can be declared with the desired annotations and added to the ObjectMapper using the addMixIn() method.

Example class:

public class MyClass
{
    private String foo;
    private String bar;
    private Integer baz;
    ...
}

Declare and add a mixin class like this. (The class can be a local class.)

@JsonIgnoreProperties(ignoreUnknown=true)
class Mixin {
    @JsonIgnore
    private Integer baz;
};
mapper.addMixIn(MyClass.class, Mixin.class);

Sample output from serialization. Note that property baz is ignored.

{
  "foo" : "value of foo",
  "bar" : "value of bar"
}

Deserializing this JSON does not cause an exception since ignoreUnknown is set in the mixin.

{
  "foo" : "value of foo",
  "bar" : "value of bar",
  "qux" : "value of qux"
}

Summary

In this article, we looked at some of the most common Jackson annotations for JSON serialization and deserialization.

Using Jackson for JSON Serialization and Deserialization

Learn how to use Jackson to serialize java objects.

1. Introduction

Jackson is one of the most common Java libraries for processing JSON. It is used for reading and writing JSON among other tasks. Using Jackson, you can easily handle automatic conversion from Java objects to JSON and back. In this article, we delve into some common Jackson usage patterns.

2. Converting a POJO to JSON

Suppose we want to convert a sample POJO (Plain Old Java Object) to JSON. An example class is defined below.

public class User {
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private List<String> emailAddrs;

    public User(String firstName,String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // standard getters and setters here
}

Converting such an object to JSON is just a couple of lines:

ObjectMapper mapper = new ObjectMapper();
User user = new User("Harrison", "Ford");
user.setEmailAddrs(Arrays.asList("harrison@example.com"));
mapper.writeValue(System.out, user);

// prints:
{"firstName":"Harrison","lastName":"Ford","dateOfBirth":null,"emailAddrs":["harrison@example.com"]}

3. Pretty Printing

Note that the default output is quite compact. Sometimes it is useful to be able to view indented output for debugging purposes. To produce properly indented output, enable the option SerializationFeature.INDENT_OUTPUT on the ObjectMapper instance before using it to serialize the object.

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
User user = new User("Harrison", "Ford");
...

The output generated now is nicely indented:

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : null,
  "emailAddrs" : [ "harrison@example.com" ]
}

4. Ignore NULL fields

How do we tell Jackson not to serialize null values (as for “dateOfBirth” above)? There are a couple of ways. You can tell the ObjectMapper to skip all NULL fields, or you can use annotations to specify per class or per field.

4.1. Configuring ObjectMapper

...
mapper.setSerializationInclusion(Include.NON_NULL);
User user = new User("Harrison", "Ford");
...

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "emailAddrs" : [ "harrison@example.com" ]
}

4.2. With an Annotation

Use the annotation @JsonInclude(Include.NON_NULL) on the target object class to eliminate serialization of all NULL fields.

@JsonInclude(Include.NON_NULL)
public class User {
    private String firstName;
    private String lastName;
    ...
}

Or on a field (or property) to disable specific NULL fields from being serialized. In the code below, dateOfBirth will be ignored if null, but not heightInM.

...
    @JsonInclude(Include.NON_NULL)
    private Date dateOfBirth;

    private Float heightInM;
...

5. Ignore Empty Arrays

When you have a class with an empty array initializer, the value is serialized as an empty JSON array.

class User {
    ...
    private List<String> phoneNumbers = new ArrayList<>();
    ...
}

// serialized to:
{
  ...
  "phoneNumbers" : [ ],
  ...
}

When you want to skip empty array declarations being output, you can use the following.

mapper.setSerializationInclusion(Include.NON_EMPTY);

6. Generate JSON String

How can we generate a JSON string representation instead of writing it directly to a file or an OutputStream? Maybe you want to store the JSON string in a database. Use the writeValueAsString() method.

// set up user
String jsonStr = mapper.writeValueAsString(user);

7. Formatting Dates

By default, Jackson prints Date fields as numeric timestamps as shown below:

Calendar c = Calendar.getInstance();
c.clear(); c.set(1942, 6, 13);
user.setDateOfBirth(c.getTime());

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : -866957400000,
  "emailAddrs" : [ "harrison@example.com" ]
}

Turning off numeric timestamp results in serialization of a date in the ISO 8601 format as shown below:

...
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
...

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "1942-07-12T18:30:00.000+0000",
  "emailAddrs" : [ "harrison@example.com" ]
}

Change the default date format by using SimpleDateFormat as shown:

...
DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy");
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
...

// prints:
{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "13-Jul-1942",
  "emailAddrs" : [ "harrison@example.com" ]
}

8. Converting JSON to POJO

Reading and converting JSON to a Java object is just as easy with Jackson. Accomplished in just a couple of lines:

ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File(jsonFile), User.class);

The target class (User in this case) needs a no-arguments default constructor defined – failing which you get this exception:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of sample.User: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

The situation is easily address by adding a no-arguments default constructor.

public class User {
    private String firstName;
    private String lastName;
    private Date dateOfBirth;
    private List<String> emailAddrs;

    public User() {}
    ...
}

9. Adjusting Date Format

As before, the date format needs to be adjusted unless it is in ISO 8601 format. Parsing the following JSON fails:

{
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "13-Jul-1942",
  "emailAddrs" : [ "harrison@example.com", null ],
}

The following exception is reported:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "13-Jul-1942": not a valid representation (error: Failed to parse Date value '13-Jul-1942': Can not parse date "13-Jul-1942": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))

Specify the date format if you are using a non-standard format as shown:

DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy");
mapper.setDateFormat(fmt);

10. Ignoring Unknown Properties

Sometimes the JSON you are trying to read might include some properties not defined in the Java class. Maybe the JSON was updated but the change is not yet reflected in the Java class. In such cases, you might end up with an exception like this:

Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "gender" (class sample.User), not marked as ignorable (6 known properties: ...)

You can tell Jackson to ignore such properties on a global level by disabling a deserialization feature as follows:

mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

Alternatively, you can use the following annotation on the class to ignore unknown properties.

@JsonIgnoreProperties(ignoreUnknown=true)
public class User {
    ...
}

11. Serializing Array of Objects to JSON

Serializing an array of objects to JSON is straightforward.

ObjectMapper mapper = new ObjectMapper();
List<User> users = new ArrayList<>();
users.add(new User(...));
users.add(new User(...));
System.out.println(mapper.writeValueAsString(users));

The array is properly serialized as shown:

[ {
  "firstName" : "Harrison",
  "lastName" : "Ford",
  "dateOfBirth" : "13-Jul-1942",
  ...},
  {
  "firstName" : "Samuel",
  "lastName" : "Jackson",
  "dateOfBirth" : "21-Dec-1948",
  ...} ]

12. Deserialize Array of Objects from JSON

Several methods are available to deserialize a JSON array to a collection of Java objects. Use whatever method suits you depending on your requirements.

Deserializing a JSON array to a Java array of objects of a particular class is as simple as:

User[] users = mapper.readValue(new File(jsonFile), User[].class);
System.out.println(mapper.writeValueAsString(users));

Using a JavaType is useful when constructing collections or parametric types.

JavaType listType = mapper
    .getTypeFactory()
    .constructCollectionType(List.class, User.class);
List<User> users = mapper.readValue(new File(jsonFile), listType);
System.out.println(mapper.writeValueAsString(users));

A third method is to create and use a TypeReference.

TypeReference ref = new TypeReference<List<User>>(){};
List<User> users = mapper.readValue(new File(jsonFile), ref);
System.out.println(mapper.writeValueAsString(users));

Summary

This article showed you how to convert Java objects to JSON and back. We also covered a few common use cases.