Processing JSON With Jackson
Processing JSON With Jackson
Processing JSON With Jackson
Processing JSON
with Jackson
Jackson is a popular suite of APIs for parsing and creating JSON objects (and more).
Chapter 11 explores the latest version of this “best JSON parser for Java.”
What Is Jackson?
Jackson is a suite of data-processing tools for Java. These tools include a streaming JSON
parser/generator library, a matching data-binding library (for converting Plain Old Java
Objects [POJOs] to and from JSON), and additional data format modules for processing
data encoded in XML and other formats.
Note Jackson was created and is being maintained by Tatu Saloranta (www.
linkedin.com/in/tatu-saloranta-b2b36/), who was inspired by the
quality and variety of XML tooling available for the Java platform and decided
to create something similar for JSON. In 2008, Saloranta founded the company
FasterXML (http://fasterxml.com), which distributed Jackson along with
other key XML-oriented products. Jackson is currently distributed on GitHub
(http://github.com/FasterXML/jackson).
Jackson consists of a core package and two other packages that depend on the core
package:
• Jackson Core: The core package supports a StAX-like streaming API for
reading and writing JSON via sequences of discrete events. This package’s
name is com.fasterxml.jackson.core. Key classes are JsonParser for
reading JSON content and JsonGenerator for writing JSON content.
323
© Jeff Friesen 2019
J. Friesen, Java XML and JSON, https://doi.org/10.1007/978-1-4842-4330-5_11
Chapter 11 Processing JSON with Jackson
Simple and full POJO-oriented data binding are supported. Simple data binding
focuses on converting to and from java.util.Maps, java.util.Lists, java.lang.
Strings, java.lang.Numbers, java.lang.Booleans, and the null reference. Full data
binding includes simple data binding but also supports converting to and from any Java
Beans (http://en.wikipedia.org/wiki/JavaBeans) bean type. Conversions are based
on property accessor conventions or annotations.
The streaming API performs better than the tree model or POJO-oriented data
binding—both APIs leverage the streaming API. However, data binding is the most
convenient and the tree model offers the most flexibility.
• jackson-annotations-2.9.7.jar
• jackson-annotations-2.9.7-javadoc.jar
324
Chapter 11 Processing JSON with Jackson
• jackson-core-2.9.7.jar
• jackson-core-2.9.7-javadoc.jar
• jackson-databind-2.9.7.jar
• jackson-databind-2.9.7-javadoc.jar
I’ve found it convenient to add all three nonJavadoc JAR files to my CLASSPATH
when compiling and running code that uses a combination of Java Core, Java Databind,
and Java Annotations:
Streaming
Streaming (also known as Incremental Processing) deserializes (reads) and serializes
(writes) JSON content as discrete events. Reading is performed by a parser that tokenizes
JSON content into tokens and associated data; writing is performed by a generator that
constructs JSON content based on a sequence of calls that output JSON tokens.
Note Streaming has the lowest memory and processing overhead, making it the
most efficient way to process JSON content. It’s used mainly by middleware and
frameworks because it’s harder to use than the tree model or data binding.
325
Chapter 11 Processing JSON with Jackson
• com.fasterxml.jackson.core.JsonParseException
• com.fasterxml.jackson.core.JsonGenerationException
Stream-Based Parsing
The abstract com.fasterxml.jackson.core.JsonParser class describes a low-level
JSON parser. It’s obtained by calling one of the com.fasterxml.jackson.core.
JsonFactory class’s overloaded createParser() methods, which take the JSON
content’s origin into account.
After creating a factory and obtaining a parser from the factory, a Java program
typically enters a loop that returns a token and does something with it per iteration. This
activity continues until the parser is closed:
while (!parser.isClosed())
{
JsonToken token = parser.nextToken();
System.out.printf("token = %s%n", token);
}
326
Chapter 11 Processing JSON with Jackson
JsonParser’s boolean isClosed() method returns true when the parser has been
closed, perhaps by invoking JsonParser’s void close() method.
JsonParser’s JsonToken nextToken() method advances the stream enough to
determine the type of the next token, returning it as a com.fasterxml.jackson.core.
JsonToken instance. When no tokens remain, null is returned.
JsonToken is an enum that identifies the basic token types used for returning the
results of parsing JSON content. Examples of its various constants are START_OBJECT ({
was seen) and END_ARRAY (] was seen).
JsonToken also provides various token classification and conversion methods.
For example, boolean isBoolean() returns true when the token describes a Boolean
value, and boolean isNumeric() returns true when the token describes a number.
Also, String asString() returns a string representation of the token (e.g., { for START_
OBJECT) or null when there is no representation (e.g., null for FIELD_NAME—an object’s
field name has been encountered).
JsonParser provides various getValueAs methods for converting a token’s value to
the method’s return type and returning that value. If conversion fails, null is returned.
For example, String getValueAsString() tries to convert the current token’s value to a
Java String, returning null when the conversion isn’t possible. The companion String
getValueAsString(String def) method returns a more meaningful default value when
conversion fails.
Listing 11-1 presents the source code to an application that demonstrates
JsonParser and JsonToken.
import java.io.File;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
327
Chapter 11 Processing JSON with Jackson
JsonParser parser =
factory.createParser(new File("person.json"));
while (!parser.isClosed())
{
JsonToken jsonToken = parser.nextToken();
if (jsonToken == null)
break;
out.printf("jsonToken = %s [%s] [%b] [%s]%n",
jsonToken, jsonToken.asString(),
jsonToken.isNumeric(),
parser.getValueAsString());
}
}
}
Listing 11-1’s main() method first creates a factory and then uses it to create a parser
for parsing a file named person.json. It then enters the previously described loop to
obtain and output tokens.
Listing 11-2 presents the JSON document that’s stored in person.json.
{
"firstName": "John",
"lastName": "Doe",
"age": 42,
"address":
{
"street": "400 Some Street",
"city": "Beverly Hills",
"state": "CA",
"zipcode": 90210
},
"phoneNumbers":
[
{
"type": "home",
328
Chapter 11 Processing JSON with Jackson
329
Chapter 11 Processing JSON with Jackson
Stream-Based Generation
The abstract com.fasterxml.jackson.core.JsonGenerator class describes a low-
level JSON generator. It’s obtained by calling one of the JsonFactory class’s overloaded
createGenerator() methods, which take the JSON content’s destination into account.
330
Chapter 11 Processing JSON with Jackson
JsonGenerator declares various methods for writing JSON content. For example,
void writeStartObject() writes the starting marker ({) for an object.
Listing 11-3 presents the source code to an application that demonstrates
JsonGenerator and methods for writing JSON content.
import java.io.File;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
331
Chapter 11 Processing JSON with Jackson
generator.writeNumberField("zipcode", 90210);
generator.writeEndObject();
generator.writeFieldName("phoneNumbers");
generator.writeStartArray();
generator.writeStartObject();
generator.writeStringField("type", "home");
generator.writeStringField("number", "310 555-1234");
generator.writeEndObject();
generator.writeStartObject();
generator.writeStringField("type", "fax");
generator.writeStringField("number", "310 555-4567");
generator.writeEndObject();
generator.writeEndArray();
generator.writeEndObject();
generator.close();
out.println("person.json successfully generated");
}
}
332
Chapter 11 Processing JSON with Jackson
Compile Listing 11-3 and run the application. You should observe the following
output:
{"firstname":"John","lastName":"Doe","age":42,"address":{"street":"400 Some
Street","city":"Beverly Hills","state":"CA","zipcode":90210},"phoneNumbers"
:[{"type":"home","number":"310 555-1234"},{"type":"fax","number":"310 555-
4567"}]}
The output is hard to read but can be improved by installing a pretty printer on the
generator. A pretty printer is an instance of a class that implements the com.fasterxml.
jackson.core.PrettyPrinter interface and that formats output to make it easier to
read. One such class is com.fasterxml.jackson.core.util.DefaultPrettyPrinter,
which employs 2-space indentation with platform-default line feeds. An instance of
this class is installed on the generator by invoking JsonGenerator’s JsonGenerator
useDefaultPrettyPrinter() method, as follows:
generator.useDefaultPrettyPrinter();
I excerpted this code fragment from a third version of the JacksonDemo application,
included in this book’s code archive. When run, that application generates a person.
json file with the following content:
{
"firstname" : "John",
"lastName" : "Doe",
"age" : 42,
"address" : {
"street" : "400 Some Street",
"city" : "Beverly Hills",
"state" : "CA",
"zipcode" : 90210
},
"phoneNumbers" : [ {
"type" : "home",
"number" : "310 555-1234"
333
Chapter 11 Processing JSON with Jackson
}, {
"type" : "fax",
"number" : "310 555-4567"
} ]
}
Tree Model
The tree model provides a mutable in-memory tree representation of a JSON document.
The com.fasterxml.jackson.databind.ObjectMapper class is used to build a tree,
whose nodes are instances of classes that descend from the abstract com.fasterxml.
jackson.databind.JsonNode class.
ObjectMapper declares several constructors for initializing an object mapper. The
noargument ObjectMapper() constructor is the easiest to use:
Once you have an object mapper, you can use it to read a JSON document into a tree,
or create a tree and write it to a JSON document.
The JsonNode readTree(File file) method deserializes JSON content from the
specified file into a tree expressed as set of JsonNode instances. The root node of this
tree is returned.
JsonNode provides various methods for accessing tree nodes. Separate methods exist
for basic traversal of JSON objects and arrays. Objects are indexed by field name and
arrays are indexed by element index. Additionally, it’s possible to use “safe” methods
that return dummy com.fasterxml.jackson.databind.node.MissingNode instances
instead of the null reference when an object or array doesn’t contain an indicated value.
Traversal methods include the following:
334
Chapter 11 Processing JSON with Jackson
335
Chapter 11 Processing JSON with Jackson
Note ArrayNode provides an int size() method that returns the number of
elements in an array. ObjectNode provides an int size() method that returns
the number of properties in an object.
Listing 11-4. Reading a JSON Document into a Tree and Traversing the Tree
import java.io.File;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
336
Chapter 11 Processing JSON with Jackson
Compile Listing 11-4 and run the application. Assuming that the current directory
also contains Listing 11-2’s person.json file, you should observe the following output:
firstName = "John"
lastName = "Doe"
age = 42
address
street = "400 Some Street"
city = "Beverly Hills"
state = "CA"
zipcode = 90210
phoneNumbers
0: type = "home", number = "310 555-1234"
1: type = "fax", number = "310 555-4567"
337
Chapter 11 Processing JSON with Jackson
ArrayNode createArrayNode()
ObjectNode createObjectNode()
Once the tree has been constructed, it can be written to a JSON document by
invoking one of ObjectMapper’s overloaded writeTree() methods:
mapper.writeTree(generator, rootNode);
Listing 11-5. Creating a Tree and Writing Its Nodes to a JSON Document
import java.io.File;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
338
Chapter 11 Processing JSON with Jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
339
Chapter 11 Processing JSON with Jackson
JsonEncoding.UTF8);
generator.useDefaultPrettyPrinter();
mapper.writeTree(generator, rootNode);
out.println("person.json successfully generated");
}
}
Compile Listing 11-5 and run the application. You should observe the following
output, along with a generated person.json file containing pretty-printed content:
D
ata Binding
The ObjectMapper class supports data binding in which JSON content is parsed to and
from Java objects such as POJOs. JSON content is deserialized into Java objects, and Java
objects are serialized into JSON content.
Deserialization is achieved by calling one of ObjectMapper’s overloaded
readValue() generic methods, which support deserialization from different sources.
For example, the following method deserializes from a file into the given Java type:
This method doesn’t support generic types because of type erasure (http://
en.wikipedia.org/wiki/Type_erasure). In contrast, the following method uses the
com.fasterxml.jackson.core.type.TypeReference<T> type to support generic types:
TypeReference is a generic abstract class that’s used to obtain full generic type
information via subclassing. I’ll show an example shortly.
Serialization is achieved by calling one of ObjectMapper’s overloaded writeValue()
methods, which support serialization to different destinations. For example, the
following method serializes a Java object to a file:
340
Chapter 11 Processing JSON with Jackson
object java.util.LinkedHashMap<String,Object>
array java.util.ArrayList<Object>
string java.lang.String
number (no fraction) java.lang.Integer, java.lang.Long, or java.math.
BigInteger (smallest applicable)
number (fraction) java.lang.Double (configurable to use java.math.BigDecimal)
true|false java.lang.Boolean
null null reference
The following example invokes readValue() to read a JSON object into a Java map:
The <?,?> syntax indicates an untyped map. To bind data into a generic map, such as
Map<String,User>, I would need to specify TypeReference as follows:
Map<String,User> rootAsMap =
mapper.readValue(src,
new TypeReference<Map<String,User>>(){});
341
Chapter 11 Processing JSON with Jackson
After creating the Java object, it can be serialized to a file or other destination by
invoking writeValue(), which is demonstrated as follows:
Listing 11-6 presents the source code to an application that demonstrates simple
data binding via these code fragments.
import java.io.File;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
342
Chapter 11 Processing JSON with Jackson
out.println(list2);
String gradesJson = "{\"John\": 86, \"Jane\": 92}";
Map<?,?> map = mapper.readValue(gradesJson,
Map.class);
out.println(map);
mapper.writeValue(new File("grades.json"), map);
}
}
After creating an ObjectMapper instance, Listing 11-6’s main() method maps a JSON
number to a Java Integer, which is output.
main() now maps a string-based JSON array to a List. Because the resulting list1
object has List<?> type, the compiler will not compile list1.add("Jupiter");. There’s
no add(String) method when the unbounded wildcard type is specified. Although this
problem could be solved by specifying List list1 (giving list1 the raw List type), any
Java object could be stored in the List, which would violate type safety. Generics must
be used to solve this problem.
The main() method next creates a list of strings and uses TypeReference to pass
String to readValue(). The compiler is able to compile list1.add("Jupiter");
because List<String> has an add(String) method.
main() now switches focus to working with Map. After creating a grades-oriented
map, it outputs the map and uses writeValue() to write the map’s content to a file
named grades.json.
Compile Listing 11-6 and run the application. You should observe the following
output:
65
[Mercury, Venus, Earth, Mars]
[Mercury, Venus, Earth, Mars]
[Mercury, Venus, Earth, Mars, Jupiter]
{John=86, Jane=92}
Furthermore, you should observe a grades.json file in the current directory. This
file should contain the following content:
{"John":86,"Jane":92}
343
Chapter 11 Processing JSON with Jackson
/*
{
"firstName": "John",
"lastName": "Doe",
"age": 42,
"address":
{
"street": "400 Some Street",
"city": "Beverly Hills",
"state": "CA",
"zipcode": 90210
},
"phoneNumbers":
[
{
"type": "home",
"number": "310 555-1234"
},
{
"type": "fax",
"number": "310 555-4567"
}
]
}
*/
public class Person
{
private String firstName;
private String lastName;
private int age;
344
Chapter 11 Processing JSON with Jackson
345
Chapter 11 Processing JSON with Jackson
346
Chapter 11 Processing JSON with Jackson
347
Chapter 11 Processing JSON with Jackson
@Override
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("firstName = " + firstName + "\n");
sb.append("lastName = " + lastName + "\n");
sb.append("age = " + age + "\n");
sb.append("address\n");
sb.append(" street = " + address.getStreet() +
"\n");
sb.append(" city = " + address.getCity() + "\n");
sb.append(" state = " + address.getState() + "\n");
sb.append(" zipcode = " + address.getZipcode() +
"\n");
sb.append("phoneNumbers\n");
for (int i = 0; i < phoneNumbers.length; i++)
{
sb.append(" type = " +
phoneNumbers[i].getType() + "\n");
sb.append(" number = " +
phoneNumbers[i].getNumber() + "\n");
}
return sb.toString();
}
}
Listing 11-7 describes a Person class that corresponds to Listing 11-2’s person.json
content. This class adheres to the Java Beans getter/setter method naming conventions.
By default, Jackson maps the fields of a JSON object to fields in a Java object by
matching the names of the JSON fields to the getter and setter methods in the Java
object. Jackson removes the get and set parts of the names of the getter and setter
methods and converts the first character of the remaining name to lowercase. For
example, in Listing 11-7, the JSON firstName field matches the Java getFirstName()
and setFirstName() getter and setter methods.
348
Chapter 11 Processing JSON with Jackson
Listing 11-8 presents the source code to an application that demonstrates full data
binding with assistance from the Person class.
import java.io.File;
import com.fasterxml.jackson.databind.ObjectMapper;
Listing 11-8’s main() method first instantiates ObjectMapper. Next, it invokes the
mapper’s readValue() method to read the contents of person.json into a Person object,
which is then printed.
349
Chapter 11 Processing JSON with Jackson
Note When using full data binding, the deserialization type must be fully
specified as something other than Object.class, which implies simple data
binding. In Listing 11-8, Person.class is passed to readValue() as the
deserialization type.
Compile Listings 11-7 and 11-8 and run the application. Assuming that the current
directory also contains Listing 11-2’s person.json file, you should observe the following
output:
firstName = John
lastName = Doe
age = 42
address
street = 400 Some Street
city = Beverly Hills
state = CA
zipcode = 90210
phoneNumbers
type = home
number = 310 555-1234
type = fax
number = 310 555-4567
A
nnotation Types
The Jackson Annotations and Jackson Databind packages provide annotation types
for influencing how JSON is read into Java objects or what JSON is generated from Java
objects. Annotation types are read-only, write-only, or read-write.
350
Chapter 11 Processing JSON with Jackson
• JsonSetter
• JsonAnySetter
• JacksonInject
• JsonDeserialize
JsonSetter
Match the annotated setter method’s name to a JSON property name when reading
JSON content into Java objects. This annotation type is useful when a Java class’s internal
property names don’t match the JSON document’s property names.
Listing 11-9 shows JsonSetter annotating a setter method in a Person class. Because
this method isn’t present in Person2, JsonSetter annotates the field instead.
Listing 11-9. Using JsonSetter to Match Java Class and JSON Document
Property Names
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.ObjectMapper;
class Person
{
private int personID = 0;
private String firstName = null;
@JsonSetter("id")
public void setPersonID(int personID)
{
this.personID = personID;
}
352
Chapter 11 Processing JSON with Jackson
@Override
public String toString()
{
return personID + ": " + firstName;
}
}
class Person2
{
@JsonSetter("id")
private int personID = 0;
private String firstName = null;
@Override
public String toString()
{
return personID + ": " + firstName;
}
}
The value passed to the @JsonSetter annotation is the name of the JSON field to
match to this setter method. Here, the name is id because that’s the name of the field in
the JSON object to be mapped to the setPersonID() setter method.
353
Chapter 11 Processing JSON with Jackson
Person2 doesn’t have a setPersonID() setter, but this isn’t a problem because
Person2 contains a getPersonID() getter, making personID deserializable (via an
implicit setter) and serializable (via getPersonID()).
Compile Listing 11-9 and run the application. You should observe the following
output:
820787: Pierre
820787: Pierre
JsonAnySetter
Call the same setter method for all unrecognized fields in the JSON object. An
unrecognized field is one that isn’t already mapped to a property or setter method in the
Java object.
Listing 11-10 shows JsonAnySetter annotating a setter method in a PropContainer
class. Without the annotation, the code would fail at runtime.
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
354
Chapter 11 Processing JSON with Jackson
" \"lastName\": \"Francois\"" +
"}";
ObjectMapper mapper = new ObjectMapper();
PropContainer pc =
mapper.readValue(jsonContent, PropContainer.class);
Iterator<Map.Entry<String, Object>> iter =
pc.iterator();
while (iter.hasNext())
{
Map.Entry<String, Object> entry = iter.next();
out.printf("Key: %s, Value: %s%n", entry.getKey(),
entry.getValue());
}
}
}
class PropContainer
{
// public String lastName;
PropContainer()
{
properties = new HashMap<>();
}
@JsonAnySetter
void addProperty(String fieldName, Object value)
{
properties.put(fieldName, value);
}
355
Chapter 11 Processing JSON with Jackson
Listing 11-10 introduces a small JSON object with id, firstName, and lastName
fields. It also introduces a PropContainer class for storing these property names and
their values.
While parsing these fields from the JSON object into the PropContainer object,
Jackson would look for setID(), setFirstName(), and setLastName() methods. Because
these methods don’t exist and there are no public id, firstName, and lastName fields,
Jackson would normally output an error message at runtime. However, the presence
of addProperty() with its @JsonAnySetter annotation causes Jackson to invoke this
method for all three fields.
Compile Listing 11-10 and run the application. You should observe the following
output:
Uncomment the line public String lastName;, recompile Listing 11-10, and rerun
the application. You should now observe the following output:
This time, Key: lastName, Value: Francois doesn’t appear, because lastName is
no longer an unrecognized property—it now exists as a property in PropContainer.
JsonCreator and JsonProperty
JsonCreator tells Jackson that the Java object has a constructor (a “creator”) that can
match, with help from JsonProperty, the JSON object’s fields to the fields of the Java
object. These annotation types are useful where it’s not possible to use the @JsonSetter
annotation. For example, immutable objects cannot have setter methods, so their initial
values must be specified in the constructor, which Listing 11-11 demonstrates.
356
Chapter 11 Processing JSON with Jackson
import com.fasterxml.jackson.databind.ObjectMapper;
class Vehicle
{
private String make, model;
@JsonCreator
Vehicle(@JsonProperty("make") String make,
@JsonProperty("model") String model,
@JsonProperty("year") int year)
{
this.make = make;
this.model = model;
this.year = year;
}
357
Chapter 11 Processing JSON with Jackson
String getMake()
{
return make;
}
String getModel()
{
return model;
}
int getYear()
{
return year;
}
}
JacksonInject
Inject (i.e., set based on an ObjectMapper-configured value) values into the parsed
objects instead of reading those values from the JSON content. For example, suppose
I add a String webURL field to the previous example’s Vehicle class. I can tell Jackson
to inject the URL of the vehicle’s manufacturer’s website into a Vehicle object by
performing the following steps:
2. Instantiate com.fasterxml.jackson.databind.
InjectableValues.Std() and invoke its addValue(Class<?>
classKey, Object value) method to add the string-based URL
to an injectable values object.
358
Chapter 11 Processing JSON with Jackson
3. Instantiate ObjectMapper.
Listing 11-12 presents the source code to an application that accomplishes these tasks.
Listing 11-12. Using JsonInject to Inject a URL into a Parsed Vehicle Object
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
359
Chapter 11 Processing JSON with Jackson
InjectableValues inject =
new InjectableValues.Std()
.addValue(String.class,
"ford.com");
Vehicle vehicle =
new ObjectMapper().reader(inject)
.forType(Vehicle.class)
.readValue(jsonContent);
out.printf("Make %s, Model %s, Year %d, URL %s%n",
vehicle.getMake(), vehicle.getModel(),
vehicle.getYear(), vehicle.webURL);
}
}
class Vehicle
{
private String make, model;
@JsonCreator
Vehicle(@JsonProperty("make") String make,
@JsonProperty("model") String model,
@JsonProperty("year") int year)
{
this.make = make;
this.model = model;
this.year = year;
}
String getMake()
{
return make;
}
360
Chapter 11 Processing JSON with Jackson
String getModel()
{
return model;
}
int getYear()
{
return year;
}
@JacksonInject
String webURL;
}
Compile Listing 11-12 and run the application. You should observe the following
output:
JsonDeserialize
Specify a custom deserializer class for a given field in a Java object. For example, suppose
a JSON document contains a color field with a string-based value such as black or red.
This document is to be deserialized into a Canvas class that declares a color field of a
Color enum type. Jackson cannot deserialize this value without help, because it cannot
convert a string to an enum instance.
The “help” that Jackson requires starts by adding annotation @JsonDeserialize
to the color field. This annotation type’s using parameter is assigned the java.
lang.Class object of the class being used to perform the custom deserialization.
Furthermore, that class must subclass the abstract com.fasterxml.jackson.databind.
JsonDeserializer<T> class, where T is the type of the annotated field.
JsonDeserializer declares an abstract T deserialize(JsonParser p,
DeserializationContext ctxt) method that Jackson calls to deserialize JSON
content into the value type that this deserializer handles. The JsonParser argument
identifies the parser of the JSON content, and the com.fasterxml.jackson.databind.
DeserializationContext argument is used to pass in configuration settings.
Listing 11-13 presents the source code to an application that implements and
demonstrates this custom deserialization example.
361
Chapter 11 Processing JSON with Jackson
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.annotation.
JsonDeserialize;
import com.fasterxml.jackson.databind.
DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
Canvas canvas =
new ObjectMapper().readerFor(Canvas.class)
.readValue(jsonContent);
System.out.printf("Color = %s%n", canvas.color);
}
}
enum Color
{
BLACK, UNKNOWN
}
class Canvas
362
Chapter 11 Processing JSON with Jackson
{
@JsonDeserialize(using = ColorDeserializer.class)
public Color color;
}
default:
return Color.UNKNOWN;
}
}
}
Compile Listing 11-13 and run the application. You should observe the following
output:
Color = BLACK
• JsonInclude
• JsonGetter
• JsonAnyGetter
363
Chapter 11 Processing JSON with Jackson
• JsonPropertyOrder
• JsonRawValue
• JsonValue
• JsonSerialize
JsonInclude
Include properties for serialization only under certain circumstances. For example, a
String property may be included only if its value isn’t the null reference.
JsonInclude can be used to annotate a field, a method parameter, a constructor
parameter, or an entire class. When a class is annotated, all properties are checked
against the annotation’s value to see if they can be serialized or not.
JsonInclude’s value is specified by assigning one of the JsonInclude.Include
enum’s constants to its value parameter. Here are three examples:
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
364
Chapter 11 Processing JSON with Jackson
class Person1
{
public int personID = 0;
public String firstName = null;
public String lastName = "Doe";
public List<String> phoneNumbers = new ArrayList<>();
}
@JsonInclude(JsonInclude.Include.ALWAYS)
class Person2
{
public int personID = 0;
public String firstName = null;
public String lastName = "Doe";
public List<String> phoneNumbers = new ArrayList<>();
}
365
Chapter 11 Processing JSON with Jackson
@JsonInclude(JsonInclude.Include.NON_EMPTY)
class Person3
{
public int personID = 0;
public String firstName = null;
public String lastName = "Doe";
public List<String> phoneNumbers = new ArrayList<>();
}
@JsonInclude(JsonInclude.Include.NON_NULL)
class Person4
{
public int personID = 0;
public String firstName = null;
public String lastName = "Doe";
public List<String> phoneNumbers = new ArrayList<>();
}
{"personID":0,"firstName":null,"lastName":"Doe","phoneNumbers":[]}
{"personID":0,"firstName":null,"lastName":"Doe","phoneNumbers":[]}
{"personID":0,"lastName":"Doe"}
{"personID":0,"lastName":"Doe","phoneNumbers":[]}
JsonGetter
When serializing a class instance, call the @JsonGetter-annotated method and serialize
the return value as the property’s value.
This annotation type is useful for Java classes that follow the jQuery (http://
en.wikipedia.org/wiki/JQuery) style for getter and setter names (such as numDoors()
instead of getNumDoors()). Consider Listing 11-15.
366
Chapter 11 Processing JSON with Jackson
import java.io.File;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.ObjectMapper;
class Person
{
private int personID = 0;
private String firstName = null;
@JsonGetter("id")
public int personID()
{
return personID;
}
367
Chapter 11 Processing JSON with Jackson
@JsonGetter("firstName")
public String firstName()
{
return firstName;
}
@Override
public String toString()
{
return personID + ": " + firstName;
}
}
820787: Pierre
368
Chapter 11 Processing JSON with Jackson
Furthermore, you should observe a pierre.json file with the following content:
{"id":820787,"firstName":"Pierre"}
JsonAnyGetter
Identify a non-static, noargument method that returns a map of properties to be
serialized to JSON. Each of the map’s key/value pairs is serialized along with regular
properties. This annotation type is the counterpart to JsonAnySetter.
Listing 11-16 presents an application that demonstrates JsonAnyGetter. This
application extends Listing 11-10, which also demonstrates JsonAnySetter.
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
369
Chapter 11 Processing JSON with Jackson
class PropContainer
{
public String lastName;
PropContainer()
{
properties = new HashMap<>();
}
@JsonAnySetter
void addProperty(String fieldName, Object value)
{
properties.put(fieldName, value);
}
@JsonAnyGetter
370
Chapter 11 Processing JSON with Jackson
Compile Listing 11-16 and run the application. You should observe the following
output:
{"lastName":"Francois","firstName":"Pierre","id":820787}
JsonPropertyOrder
Specify the order in which a Java object’s fields should be serialized to JSON content.
This annotation type makes it possible to override the default top-down order with a
different order. Consider Listing 11-17.
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
371
Chapter 11 Processing JSON with Jackson
class SomeClass1
{
public int a = 1, b = 2, c = 3, d = 4;
public String e = "e";
}
@JsonPropertyOrder({"e","d","c","b","a"})
class SomeClass2
{
public int a = 1, b = 2, c = 3, d = 4;
public String e = "e";
}
372
Chapter 11 Processing JSON with Jackson
Compile Listing 11-17 and run the application. You should observe the following
output:
serialization successful
{"a":1,"b":2,"c":3,"d":4,"e":"e"}
{"e":"e","d":4,"c":3,"b":2,"a":1}
JsonRawValue
Tell Jackson that the annotated Java method or field should be serialized as is. A string
property’s value is serialized without the surrounding quote characters. Consider
Listing 11-18.
import java.io.File;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.databind.ObjectMapper;
373
Chapter 11 Processing JSON with Jackson
class Driver1
{
public String name = "John Doe";
public String vehicle = "{ \"make\": \"Ford\", " +
"\"model\": \"F150\", " +
"\"year\": 2008";
}
class Driver2
{
public String name = "John Doe";
@JsonRawValue
public String vehicle = "{ \"make\": \"Ford\", " +
"\"model\": \"F150\", " +
"\"year\": 2008";
}
Compile Listing 11-18 and run the application. You should observe the following
output:
serialization successful
Unlike the previous output, this output reveals that the value of Driver2’s vehicle
property is serialized as part of the JSON object structure. It’s not serialized to a string in
the JSON object’s vehicle field, as the previous output shows.
374
Chapter 11 Processing JSON with Jackson
JsonValue
Delegate the serialization of a Java object to one of its methods, which must take no
arguments and which must return a value of a scalar type (such as String or java.lang.
Number) or any serializable type (such as java.util.Collection or Map).
For a noargument method with a String return type, Jackson escapes any double
quotation marks inside the returned String, making it impossible to return a full JSON
object. However, that task can be accomplished by using @JsonRawValue.
Listing 11-19 demonstrates JsonValue.
Listing 11-19. Using JSonValue to Serialize Props1 and Props2 Objects via Their
toJson() Methods
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;
375
Chapter 11 Processing JSON with Jackson
class Props1
{
public String a = "A";
public String b = "B";
@JsonValue
public String toJSON()
{
return a + "\"-\"" + b;
}
}
class Props2
{
private Map<String, String> props = new HashMap<>();
Props2()
{
props.put("a", "A'A'\"A\"");
props.put("b", "B'B'\"B\"");
}
@JsonValue
public Map<String, String> toJSON()
{
return props;
}
}
Compile Listing 11-19 and run the application. You should observe the following
output:
serialization successful
"A\"-\"B"
{"a":"A'A'\"A\"","b":"B'B'\"B\""}
376
Chapter 11 Processing JSON with Jackson
This output reveals that double quotation marks are escaped even in noargument
methods with return types other than String.
JsonSerialize
Specify a custom serializer class for a given field in a Java object. For example, suppose a
Java object contains a color field whose value is an enum instance such as Color.BLACK.
This object is to be serialized to a JSON document whose color field is associated with a
string-based value such as black or red. Jackson cannot serialize this value without help,
because it cannot convert an enum instance to a string.
The “help” that Jackson requires starts by adding a @JsonSerialize annotation to
the color field. This annotation type’s using parameter is assigned the Class object of
the class being used to perform the custom serialization. Furthermore, that class must
subclass the abstract com.fasterxml.jackson.databind.JsonSerializer<T> class,
where T is the type of the annotated field.
JsonSerializer declares an abstract void serialize(T value, JsonGenerator
gen, SerializerProvider serializers) method that Jackson calls to serialize the
value type that this serializer handles to JSON content. The JsonGenerator argument
identifies the generator of the JSON content, and the com.fasterxml.jackson.
databind.SerializerProvider argument is used when serializing complex object
graphs.
Listing 11-20 presents the source code to an application that refactors Listing 11-13
to implement and demonstrate this custom serialization example.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
377
Chapter 11 Processing JSON with Jackson
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
Canvas canvas =
new ObjectMapper().readerFor(Canvas.class)
.readValue(jsonContent);
out.printf("Color = %s%n", canvas.color);
new ObjectMapper().writeValue(new File("color.json"),
canvas);
}
}
enum Color
{
BLACK, UNKNOWN
}
class Canvas
{
@JsonDeserialize(using = ColorDeserializer.class)
@JsonSerialize(using = ColorSerializer.class)
public Color color;
}
378
Chapter 11 Processing JSON with Jackson
default:
return Color.UNKNOWN;
}
}
}
379
Chapter 11 Processing JSON with Jackson
default:
jsonGenerator.writeString("unknown");
}
}
}
Compile Listing 11-20 and run the application. You should observe the following
output:
Color = BLACK
Furthermore, you should observe a generated color.json file with the following
content:
{"color":"black"}
• JsonIgnoreType
• JsonAutoDetect
JsonIgnore and JsonIgnoreProperties
JsonIgnore tells Jackson to ignore a certain property (field) of a Java object. The property
is ignored when reading JSON content to Java objects and when writing Java objects to
JSON content. JsonIgnoreProperties tells Jackson to ignore a list of properties of a Java
class. The @JsonIgnoreProperties annotation is placed above the class declaration
instead of above each property (field) to be ignored. These closely related annotation
types are demonstrated in Listing 11-21.
380
Chapter 11 Processing JSON with Jackson
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.
JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
381
Chapter 11 Processing JSON with Jackson
sa2.accountOwnerName);
out.printf("balanceInCents = %d%n",
sa2.balanceInCents);
sa3 = mapper.readValue(new File("sa1.json"),
SavingsAccount3.class);
out.printf("bankID = %s%n", sa3.bankID);
out.printf("accountOwnerName = %s%n",
sa3.accountOwnerName);
out.printf("balanceInCents = %d%n",
sa3.balanceInCents);
}
}
class SavingsAccount1
{
public String bankID;
public String accountOwnerName;
public long balanceInCents;
SavingsAccount1()
{
}
class SavingsAccount2
{
@JsonIgnore
public String bankID;
public String accountOwnerName;
public long balanceInCents;
382
Chapter 11 Processing JSON with Jackson
SavingsAccount2()
{
}
@JsonIgnoreProperties({"bankID", "accountOwnerName"})
class SavingsAccount3
{
public String bankID;
public String accountOwnerName;
public long balanceInCents;
SavingsAccount3()
{
}
383
Chapter 11 Processing JSON with Jackson
Compile Listing 11-21 and run the application. You should observe the following
output:
bankID = 101
accountOwnerName = John Doe
balanceInCents = 50000
bankID = null
accountOwnerName = John Doe
balanceInCents = 50000
bankID = null
accountOwnerName = null
balanceInCents = 50000
The null values are the result of bankID and accountOwnerName being set to null
because of @JsonIgnore and @jsonIgnoreProperties, even though this information is
stored in sa1.json.
The generated sa1.json file contains the following content:
{"bankID":"101","accountOwnerName":"John Doe","balanceInCents":50000}
{"accountOwnerName":"John Doe","balanceInCents":50000}
{"balanceInCents":50000}
JsonIgnoreType
All properties of an annotated type are to be ignored during serialization and
deserialization. This annotation type is demonstrated in Listing 11-22, where it’s used to
prevent Address properties from being serialized/deserialized.
import java.io.File;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.databind.ObjectMapper;
384
Chapter 11 Processing JSON with Jackson
class Person1
{
public String name;
385
Chapter 11 Processing JSON with Jackson
class Person2
{
public String name;
@JsonIgnoreType
public static class Address
{
public String street;
public String city;
}
Compile Listing 11-22 and run the application. You should observe the following
output:
The exception is the result of address being set to null because of @JsonIgnoreType,
even though the address information is stored in person1.json.
The generated person1.json file contains the following content:
{"name":"John Doe"}
386
Chapter 11 Processing JSON with Jackson
JsonAutoDetect
Tell Jackson to include non-public properties when reading and writing Java objects.
This annotation type declares various visibility elements for determining the
minimum visibility level (such as public or protected) for auto-detection. For example,
fieldVisibility specifies the minimum visibility required for auto-detecting member
fields. One of the JsonAutoDetect.Visibility enum constants is assigned to this
element. This enum enumerates possible visibility thresholds (minimum visibility) that
can be used to limit which methods (and fields) are auto-detected.
Listing 11-23 demonstrates JsonAutoDetect and its nested Visibility enum.
import java.io.File;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.ObjectMapper;
sc3.print();
}
}
class SomeClass1
{
public int a;
private int b;
protected int c;
SomeClass1()
{
}
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility
.PROTECTED_AND_PUBLIC)
class SomeClass2
{
public int a;
private int b;
protected int c;
SomeClass2()
{
}
388
Chapter 11 Processing JSON with Jackson
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility
.ANY)
class SomeClass3
{
public int a;
private int b;
protected int c;
SomeClass3()
{
}
389
Chapter 11 Processing JSON with Jackson
a = 1, b = 0, c = 0
a = 1, b = 0, c = 3
a = 1, b = 2, c = 3
{"a":1}
{"a":1,"c":3}
{"a":1,"b":2,"c":3}
390
Chapter 11 Processing JSON with Jackson
{
"firstname" : "John",
"lastName" : "Doe",
"age" : 42,
"address" : {
"street" : "400 Some Street",
"city" : "Beverly Hills",
"state" : "CA",
"zipcode" : 90210
},
"phoneNumbers" : [ {
"type" : "home",
"number" : "310 555-1234"
}, {
"type" : "fax",
"number" : "310 555-4567"
} ]
}
391
Chapter 11 Processing JSON with Jackson
{
"firstname" : "John",
"lastName" : "Doe",
"age" : 42,
"address" :
{
"street" : "400 Some Street",
"city" : "Beverly Hills",
"state" : "CA",
"zipcode" : 90210
},
"phoneNumbers" :
[
{
"type" : "home",
"number" : "310 555-1234"
},
{
"type" : "fax",
"number" : "310 555-4567"
}
]
}
import java.io.IOException;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.PrettyPrinter;
392
Chapter 11 Processing JSON with Jackson
import com.fasterxml.jackson.core.util.Instantiatable;
393
Chapter 11 Processing JSON with Jackson
generator.close();
}
}
class MyPrettyPrinter
implements PrettyPrinter, Instantiatable<MyPrettyPrinter>
{
private final String LINE_SEP =
getProperty("line.separator");
@Override
public MyPrettyPrinter createInstance()
{
return new MyPrettyPrinter();
}
@Override
public void writeStartObject(JsonGenerator jg)
throws IOException
{
if (!isNewline)
newline(jg);
jg.writeRaw('{');
++indent;
isNewline = false;
}
@Override
public void beforeObjectEntries(JsonGenerator jg)
throws IOException
{
newline(jg) ;
}
394
Chapter 11 Processing JSON with Jackson
@Override
public void
writeObjectFieldValueSeparator(JsonGenerator jg)
throws IOException
{
jg.writeRaw(" : ");
isNewline = false;
}
@Override
public void writeObjectEntrySeparator(JsonGenerator jg)
throws IOException
{
jg.writeRaw(",");
newline(jg);
}
@Override
public void writeEndObject(JsonGenerator jg,
int nrOfEntries)
throws IOException
{
--indent;
newline(jg);
jg.writeRaw('}');
isNewline = indent == 0;
}
@Override
public void writeStartArray(JsonGenerator jg)
throws IOException
{
newline(jg);
jg.writeRaw("[");
++indent;
isNewline = false;
}
395
Chapter 11 Processing JSON with Jackson
@Override
public void beforeArrayValues(JsonGenerator jg)
throws IOException
{
newline(jg);
}
@Override
public void writeArrayValueSeparator(JsonGenerator jg)
throws IOException
{
jg.writeRaw(", ");
isNewline = false;
}
@Override
public void writeEndArray(JsonGenerator jg,
int nrOfValues)
throws IOException
{
--indent;
newline(jg);
jg.writeRaw(']');
isNewline = false;
}
@Override
public void writeRootValueSeparator(JsonGenerator jg)
throws IOException
{
jg.writeRaw(' ');
}
396
Chapter 11 Processing JSON with Jackson
jg.writeRaw(" ");
isNewline = true;
}
}
generator.setPrettyPrinter(new MyPrettyPrinter());
Compile Listing 11-24 and run the application. You should observe the following
output:
{
"firstname" : "John",
"lastName" : "Doe",
"age" : 42,
"address" :
{
"street" : "400 Some Street",
"city" : "Beverly Hills",
"state" : "CA",
"zipcode" : 90210
},
397
Chapter 11 Processing JSON with Jackson
"phoneNumbers" :
[
{
"type" : "home",
"number" : "310 555-1234"
},
{
"type" : "fax",
"number" : "310 555-4567"
}
]
}
• CANONICALIZE_FIELD_NAMES
• FAIL_ON_SYMBOL_HASH_OVERFLOW
• INTERN_FIELD_NAMES
• USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING
JsonFactory provides four methods to enable, disable, and interrogate the state of
these features:
• JsonFactory enable(JsonFactory.Feature f)
• boolean isEnabled(JsonFactory.Feature f)
398
Chapter 11 Processing JSON with Jackson
• ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER
• ALLOW_COMMENTS
• ALLOW_MISSING_VALUES
• ALLOW_NON_NUMERIC_NUMBERS
• ALLOW_NUMERIC_LEADING_ZEROS
• ALLOW_SINGLE_QUOTES
• ALLOW_TRAILING_COMMA
• ALLOW_UNQUOTED_CONTROL_CHARS
• ALLOW_UNQUOTED_FIELD_NAMES
• ALLOW_YAML_COMMENTS
• AUTO_CLOSE_SOURCE
• IGNORE_UNDEFINED
• INCLUDE_SOURCE_IN_LOCATION
• STRICT_DUPLICATE_DETECTION
JsonParser provides four methods to enable, disable, and interrogate the state of
these features:
• JsonParser configure(JsonParser.Feature f, boolean state)
• JsonParser disable(JsonParser.Feature f)
• JsonParser enable(JsonParser.Feature f)
• boolean isEnabled(JsonParser.Feature f)
• AUTO_CLOSE_TARGET
• ESCAPE_NON_ASCII
399
Chapter 11 Processing JSON with Jackson
• FLUSH_PASSED_TO_STREAM
• IGNORE_UNKNOWN
• QUOTE_FIELD_NAMES
• QUOTE_NON_NUMERIC_NUMBERS
• STRICT_DUPLICATE_DETECTION
• WRITE_BIGDECIMAL_AS_PLAIN
• WRITE_NUMBERS_AS_STRINGS
JsonGenerator provides four methods to enable, disable, and interrogate the state of
these features:
• JsonGenerator disable(JsonGenerator.Feature f)
• JsonGenerator enable(JsonGenerator.Feature f)
• boolean isEnabled(JsonGenerator.Feature f)
400
Chapter 11 Processing JSON with Jackson
• boolean isEnabled(JsonFactory.Feature f)
• boolean isEnabled(JsonGenerator.Feature f)
• boolean isEnabled(JsonParser.Feature f)
mapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
If you recall, I disabled this feature to prevent mapper.writeValue() from closing the
System.out stream.
EXERCISES
The following exercises are designed to test your understanding of Chapter 11’s content:
1. Define Jackson.
3. True or false: Jackson supports only full data binding and POJO data binding.
5. True or false: Streaming is the least efficient way to process JSON content.
7. After you obtain a parser, how do use the parser to parse JSON content?
9. After you obtain a generator, how do you use the generator to generate JSON
content?
10. True or false: The tree model provides a mutable in-memory tree representation
of a JSON document.
13. What is the difference between JsonNode get(int index) and JsonNode
path(int index)?
401
Chapter 11 Processing JSON with Jackson
17. How does simple data binding differ from full data binding?
18. By default, how does Jackson map the fields of a JSON object to fields in a
Java object?
19. True or false: A getter method makes a non-public field serializable only.
22. What’s the difference between a stateful and a stateless pretty printer class?
24. Modify Listing 11-3 so that numbers are written out as strings.
S
ummary
Jackson is a suite of data-processing tools for Java. These tools include a streaming
JSON parser/generator library, a matching data-binding library (for converting Plain
Old Java Objects—POJOs—to and from JSON), and additional data format modules for
processing data encoded in XML and other formats.
Jackson consists of core, databind, and annotations packages. The core package
supports a StAX-like streaming API for reading and writing JSON via sequences of
discrete events. The databind package supports a DOM-like tree model that provides a
mutable in-memory tree representation of a JSON document. The annotations package
provides public core annotation types, most of which are used to configure how data
binding (mapping) works.
The Jackson Core and Jackson Databind packages support the consumption and
creation of JSON documents. They offer various types related to streaming, the tree
model, and POJO-oriented data binding.
402
Chapter 11 Processing JSON with Jackson
403