Jackson 2.14 sneak peek

@cowtowncoder
6 min readAug 9, 2022

It has been a while since Jackson 2.13 was released — about 9 months.
Since then there has been steady progress, with about 50 fixes, improvements and new features (see in-progress 2.14 release notes for details).
The plan is to get the first Release Candidate (2.14.0-rc1) out during August 2022. Let’s have a look at some of interesting improvements, features that will be included.

Another “minor minor” release

One thing to note is that as with 2.13 — and different from Jackson 2.12– version 2.14 can be considered another “smaller” minor release: most improvements are incremental and there is no wide expansion of functionality (no new format or datatype modules)

Most notable improvements can be found in following areas:

  1. Improvements to JSON parser (performance, support for more “non-standard” content reading, alternate non-blocking input source)
  2. Datatype-specific configurability for ObjectMapper (and ObjectReader / ObjectWriter)
  3. More ways to use JsonNode (Tree Model)

Compatibility: Java 8 now baseline for jackson-core, jackson-jr

2.14 should be highly compatible with 2.13 even beyond the usual minor-to-minor compatibility. The only notable compatibility change is that Java 8 will now be the minimum requirement for jackson-core (streaming parser/generator) and jackson-jr modules. Jackson-annotations will remain the one and only module that still only requires Java 6 (until Jackson 3).

This is not expected to have much impact for users but simplifies maintenance: users (if any) that require Java 6 or Java 7 compatibility will still be able to use 2.13.x patches.

Feature: Improved @JsonAnySetter handling

Up until 2.14, @JsonAnySetter has been usable either for

  1. 2-argument setter method (which gets called once per otherwise unmapped property), OR
  2. Map<String, T> valued non-null Field (in which case all otherwise unmapped properties are bound to an existing Map

But there had been long-running desire to extend this functionality to allow use of:

  • Non-initialized (null) Map wherein Map is dynamically created if (and only if) needed (databind#3559)
  • ObjectNode (JsonNode that represents JSON Object value) value Field (databind#3394)

So now you can use constructs like:

public class MapAnyBean {
// no need to initialize if one of "well-known" Map types:
@JsonAnySetter
public Map<String, Object> values;
}
public class JsonNodeBean {
// May initialize but no need to:
@JsonAnySetter
public ObjectNode valuesAsNodes;
}

neither of which would have worked with Jackson 2.13 or earlier.

Feature: ByteBuffer-backed non-blocking JSON parser

As is well-known (or is it? :-) ) in addition to its default blocking I/O based parser, Jackson ALSO provides an efficient, fully-featured, world-class non-blocking (aka “async”) JSON parser implementation (since Jackson 2.9, see “Non-Blocking Reading” section).
But so far it has required content to be fed as raw byte arrays (byte[]).

A long-standing — and Highly Voted! — wish (core#478) is now fulfilled: Jackson 2.14 will provide an alternate ByteBuffer backed implementation — it works the same way except for different NonBlockingInputFeeder (ByteBufferFeeder instead of ByteArrayFeeder). Refactoring work also means that it should now be much easier to add support for other non-blocking input sources.

Feature: Support for more Non-Standard “JSON” constructs

Over time there have been many requests to support decoding of “almost JSON” (or “JSON-like”) content; especially for things like:

  • Optional/extra separators (trailing comma, missing values)
  • Flexible number representations
  • Optional comments

Existing options are mostly configurable using JsonReadFeature (see jackson-core wiki for links to specific Feature options). Jackson 2.14 will add 2 more settings that can be useful for things like (badly named) “JSON5” content:

  • JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS (jackson-core#773) to allow floating-point numbers like 124. and -8.
  • JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS (jackson-core#774) to allow numbers with leading plus sign, like +25 and +0.17

Note: as per long-standing policy, all deviations from the strict JSON specification require to be explicitly enabled — they are not allowed by default.

Feature: FASTER floating-point number reading and writing

One area where JSON content handling has significant performance disadvantage compared to binary formats is that of floating-point (non-integer) number reading and writing.
Jackson 2.14 will now incorporate alternative, high(er)-performance algorithm (“Schubfac”) for reading and/or writing FP numbers in JSON content.

Features in question are:

  1. StreamReadFeature.USE_FAST_DOUBLE_PARSER (jackson-core#577)
  2. StreamWriteFeature.USE_FAST_DOUBLE_WRITER (jackson-core#749)

and are usually enabled by configuring JsonFactory like so:

JsonFactory f = JsonFactory.builder()
.enable(StreamReadFeature.USE_FAST_DOUBLE_PARSER)
.enable(StreamWriteFeature.USE_FAST_DOUBLE_WRITER)
.build();

One thing to note is that there is a possibility that the conversion between textual value and the internal Java floating-point value (for float, double; BigDecimal is not affected) may differ from that of conversion implemented by JDK: this is why the new read/write implementation is not enabled by default and has to be explicitly enabled by users.

Note, too, that in near future support will be added for other textual format backends too (CSV, Properties, TOML, XML, YAML). It is possible that this will be even included in 2.14 release.

We also hope to get some performance measurement numbers on difference these settings make — stay tuned!

Feature: datatype-specific configurability (JsonNodeFeature, EnumFeature?)

One “Bigger” feature, explained in JSTEP-7 proposal, is the ability to extend ObjectMapper configurability since existing (pre-2.14) choices are limited to:

  • Generic databinding Features (DeserializationFeature, SerializationFeature, MapperFeature), affecting all content
  • Generic and Format-specific low-level stream read/write features (like JsonReadFeature, CsvWriteFeature etc)
  • Fully format-specific extensions through either format-specific schemas (CsvSchema, JavaPropsSchema) or settings configurable only through format-mapper builder (CsvMapper.builder().setCSVSpecificThings(123) )
  • Some serializers/deserializers may require direct configuration for exact specifics of Java class in question

But over time it has become clear that there is need for configuring handling of “categories of datatypes” like Date/Time; or “all Enum types”; or “Tree Model” types (JsonNode subtypes); something sort of close to DeserializationFeature / SerializationFeature but not extending to all datatypes.

So Jackson 2.14 now includes a mechanism with which we can easily add:

  • Different kinds of DatatypeFeature implementations (as special kinds of Enums)
  • New entries to said implementations

with very little work within jackson-databind itself: meaning that new kinds of Features and new Feature entries can and will be added incrementally in newer version.

Jackson 2.14 starts with a small set of features, and the expectation is that many more options will be added with later minor versions.
Initially included we have just 2 new options:

  • JsonNodeFeature.READ_NULL_PROPERTIES (databind#3421)— whether null values from incoming JSON will result in NullNode entries being added in ObjectNode (enabled) or not (disabled)
  • JsonNodeFeature.WRITE_NULL_PROPERTIES (databind#3476)— whether NullNode valued entries of ObjectNode will be written out as JSON nulls (enabled) or skipped (disabled)

and configuration will use the standard idiom of:

// May configure ObjectMapper default settings:
ObjectMapper mapper = JsonMapper.builder()
.disable(JsonNodeFeature.READ_NULL_PROPERTIES)
.build();
// and/or change for ObjectReaders/-Writers
JsonNode rootNode = mapper.readerFor(JsonNode.class)
.enable(JsonNodeFeature.READ_NULL_PROPERTIES)
.readValue(inputJson);
String json = mapper.writer()
.disable(JsonNodeFeature.WRITE_NULL_PROPERTIES)
.writeValueAsString(rootNode);

Same will be true for other DatatypeFeatures added — like EnumFeature (which may even still be added in 2.14 if I have time to) or DateTimeFeature (which is highly requested but may need to wait until 2.15).

The main benefit is really for Jackson development team as it will be trivially easy to quickly add new configuration options. There is also minimal overhead for storing these configuration settings (they are backed by bit fields, similar to all other XxxFeature options)

Feature: JsonNode.withObject(JsonPointer) / .withArray(JsonPointer)

Use of JsonPointer with JsonNode is quite convenient for read access:

JsonNode doc = mapper.readTree(docSource);
// From {"users: [
// { "name" : "Bob", "age" : 42 }
// ])
int age = doc.at("/users/0/age").asInt();

but so far it has not been possible to easily MODIFY Tree Model — based documents.

But Jackson 2.14 adds two new methods (see [databind#1980]) to change this:

  1. JsonNode.withObject(JsonPointer) — will traverse specified path and ensure that at its end there will be a JSON Object (ObjectNode), which is returned
  2. JsonNode.withArray(JsonPointer) — will traverse specific path and ensure that at its end there will be a JSON Array (ArrayNode), which is returned

(as well as 2 overloads for more configurability wrt. what overwrites of existing nodes are allowed)

These methods will allow usage where JsonPointer can be used to indicate Path that either exists OR — if not — WILL exist (that is, be created). So we could construct document like above with:

ObjectNode root = mapper.createObjectNode();
root.withObject(JsonPointer.compile("/users/0")).put("age", 42);

as well as replace existing value(s).

Other Modules

As per 2.14 Release Notes there are definitely more noteworthy changes than I have time to write for so if interested, feel free to poke around.

But of special note is Jackson Scala module which has its own release notes:

That’s All, Folks!

And there we have it. I hope to get the first (and possibly only) Release Candidate out Very Soon Now — and the official 2.14.0 out by September, 2022.

--

--

@cowtowncoder

Open Source developer, most known for Jackson data processor (nee “JSON library”), author of many, many other OSS libraries for Java, from ClassMate to Woodstox