Best bits of Jackson 2.6

Continuing with the retroactive analysis of new features that were introduced in Jackson releases (but that I haven’t yet blogged about) let’s now look at 2.6. As usual, check out full release notes for item-by-item overview; I will focus on things that to me look like the most important things.

“Major minor” release

After a bit more limited 2.5 release, 2.6 is actually a much bigger upgrade, functionality-wise.

Since 2.6.0 was released almost exactly 1 year ago from now, there have been a few updates, 2.6.6 being the latest, and most likely final patch release. Many frameworks have only recently upgraded to this version, but major JAX-RS containers (DropWizard, Spring Boot) are already based on Jackson 2.6.

Compatibility

No changes were made to compatibility (Java 6 baseline; Scala 2.10).

New/Significantly Improved Modules

2.6 adds 2 new modules:

  • Protobuf data format module for reading/writing Google’s protobuf (v2) encoded data (either passing textual `protoc` or generating definition from POJOs)
  • OSGi module for allowing injection of OSGi provided values (via `@JacksonInject` annotations), similar to existing Guice module

Of existing modules, biggest improvements (aside from `jackson-databind` that tends to get most updates) were for JSON Schema generator, Joda datatype and Guava datatype modules.

Notable new features

As usual this is a subjective selection, but here are my top picks for new 2.6 features.

`@JsonFormat(with=…, without=…)`

The first noteworthy improved feature was theaddition of `with` and `without` properties for existing `@JsonFormat` annotation. These allow enabling and/or disabling various features for individual properties, to override global default settings.
For example, to support “XML-like” notation where a List-valued property with just single value is written as single element (not List) you could use:

public class POJO {
@JsonFormat(with = JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
public List<String> values;
}

instead of globally enabling `SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED`.

With 2.6, following features were defined and supported:

  • ACCEPT_SINGLE_VALUE_AS_ARRAY
  • WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS
  • WRITE_DATES_WITH_ZONE_ID
  • WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED
  • WRITE_SORTED_MAP_ENTRIES

Feature was designed to be extensible from API perspective; new choices may and will be added in `JsonFormat.Feature` enumeration (in fact, Jackson 2.8 will introduce one more — but more about this later on).
See JsonFormat.Feature Javadocs for details on specific behavior of above-mentioned features.

`@JsonProperty(required=true)` for Constructors

While `required` property has existed in `@JsonProperty` for a while, it has only been passed as metadata for things like JSON Schema generation.

But with 2.6 databind, required-ness will actually be enforced when using Creators (constructors and static factory methods annotated with `@JsonCreator`) so that if no explicit value comes from input (or provided via `@JacksonInject` if property has one), exception will be thrown.
This is useful for simple sanity-checking; more complex validation is still best done using components like Bean Validation API.

`ObjectReader.at(“/sender/firstName”)`

Sometimes when reading JSON content (or any of the other formats Jackson supports), only a small subset of it is of interest and it would be nice to just extract that small piece and ignore the rest. This is especially nice if object graph is big and mapping it would require modeling many unnecessary types.

Although use of Tree Model (binding as `JsonNode`) allows access both by explicit traversal and use of JSON Pointer expressions, this is bit wasteful as the whole document will still be read and full tree constructed.

Jackson 2.6 adds a simple but powerful mechanism that allows reading of just a sub-tree, by specifying JSON Pointer expression that points to the root of the sub-tree to bind. For example, assuming JSON document like:

{
“title”: “JSON Pointer”,
"authors”: [ {"name": "P. Bryan"},
{"name": "K. Zyp"},
{"name": "M. Nottingham"}
]
}

we could get the name of the first author like so:

public class Name {
public String name;
}
...
Name firstAuthor = mapper.readerFor(Name.class)
.at("/authors/0")
.readValue(doc);
// or for String just
String name = mapper.readerFor(String.class)
.at("/authors/0/name")
.readValue(doc);

Aside from being more concise than alternative of first reading a tree, this is also more efficient: underlying streaming JSON parser will skip over content that does not match the JSON Pointer expression and only expose matching content, which databinder will then bind to the type you specify.

Read-only/write-only properties

Sometimes it is useful to have properties that are only included in serialization (“write-only”), or that is not written but is accepted when reading content (“read-only”). While this has been possible by specifically structuring POJOs such that either only getter or setter exists, or by adding combination of `@JsonProperty` and `@JsonIgnore`, this has been cumbersome.

2.6 adds two complementary mechanisms for directly specifying intended behavior without having to change setters/getters, or adding multiple annotations.

First mechanism is a minor modification to behavior of `@JsonIgnoreProperties`, to specify that only getters” or “setters” of named properties are to be ignred — that is, to essentially only apply ignoral on serialization but not deserialization (that is, prevent writing out of named properties), or vice versa (prevent reading values from content, but write them normally). For example:

// will serialize with "x", but never modify when reading content
@JsonIgnoreProperties(value={"x"}, allowGetters=true, allowSetters=false)
public class Immutable {
int x;
  public int getX() { return x; }
}
// will read and bind "x" and "y" from content, but not serialize
@JsonIgnoreProperties(value={"x","y"}, allowGetters=false, allowSetters=true)
public class WriteOnly {
int x, y;
  @JsonProperty("desc")
public String toString() {
return String.format("[%d,%d]", x, y);
}
}

Second mechanism applies directly to the property itself, using newly added `access` property of `@JsonProperty` annotation:

public class ImmutablePoint {
@JsonProperty(access=JsonProperty.Access.READ_ONLY)
public int x;
@JsonProperty(access=JsonProperty.Access.READ_ONLY)
public int y;
}

and in this case allows specifying of visibility for getter and/or setter such that:

  • READ_ONLY: Java property may be read (getter is considered visible), and so property is serialization-only
  • WRITE_ONLY: Java property may be written (setter is considered visible), and so property is deserialization-only
  • READ_WRITE: Both setter and getter are visible (even if they otherwise would not be)
  • AUTO: Default visibility rules are used, setter/getter may or may not be visible (default value for `@JsonProperty.access`

JsonPointer-filtering for JsonParser/JsonGenerator

We already saw the powerful `at(jsonPointer)` locator for `ObjectReader` that allows skipping of “uninteresting” part of input content. Under the hood this is actually implemented viaaddition of `FilteringGeneratorDelegate`, `FilteringParserDelegate` and `TokenFilter` interfaces (pluggable to `JsonGenerator` and `JsonParser`, respectively), as well as `JsonPointerBasedFilter` implementation of the filter. End result here is that it is easy to plug in JSON Pointer based filter to get a “sub-stream” of content; this is what `ObjectReader` uses for its operation.

A full example is bit involved; if interested please look at unit tests at:

  • src/test/java/com/fasterxml/jackson/core/filter/JsonPointerGeneratorFilteringTest.java
  • src/test/java/com/fasterxml/jackson/core/filter/JsonPointerParserFilteringTest.java

Note that un addition to the default filter implementation it is possible to implement most customized, powerful filters for both reading and writing (JSON) content; content matching does not have to be limited to sub-trees (it could just, say, filter out specific properties).

ReferenceType for improved Option(al) support

Although Jackson has supported various “Optionals” (of Guava, Scala and Java 8; and similar `AtomicReference` by earlier JDKs), support has had some limitations since core `jackson-databind` has not been aware of specific nature of such “container-like, ”zero-or-one element container-like” type”. But with 2.6, a new `JavaType` was added: `ReferenceType`. Now extension modules may (and should) refine these Optional types as `ReferenceType`s, and doing this enables databind to add much of foundational support directly. This will both simplify module implementations and improve interoperability with other features such as polymorphic handling of contained values.

In addition to `JavaType` addition, there is also new value for `JsonInclude.Include` for Optional types, `NON_ABSENT`, used like:

public class Stuff {
@JsonInclude(JsonInclude.Include.NON_ABSENT)
public AtomicReference<String> nameMaybe;
}

so that property `nameMaybe` will be included in serialization if (and only if!) not only the reference is not null, but also that the value has been set to non-null String.

Error Recovery for `ObjectReader.readValues()`

And last but not least: handling of databinding problems (reported as `JsonMappingException`s, or some as `JsonParseException`s), when using `ObjectReader.readValues()` has been improved so that it is often possible to recover reading by skipping mismatching tokens.

That is: if you catch and process (or ignore) a `JsonProcessingException` (any exception type Jackson uses to signal problem it has with input) for the value you tried to read, you can still try to read following entries. Whether recovery succeeds (and how much of intervening content is skipped) depends on type of error, and possibly specifics of data format (some binary formats, for example, can not gracefully recover). But for many textual data formats this works pretty well: and specifically when reading CSV content it is often possibly to just skip “broken” lines but continue processing:

CsvMapper mapper = new CsvMapper();
MappingIterator<Entry> it = mapper.readerWithSchemaFor(Entry.class)
.readValues(new File("stuff.csv"));
int fails = 0;
int line = 0;
while (it.hasMoreValues()) {
++line;
try {
entry = it.nextValue();
// process
} catch (JsonProcessingException e) {
log.warn("Problem on line #%d: %s", line, e.getMessage());
if (++fails == 10) {
log.error("Too many broken lines (10), quitting!");
break;
}
}
}

which is especially important when input content tends to have errors, like additional values, or incorrectly escaped values.

Like what you read? Give @cowtowncoder a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.