Serializing Java POJO as JSON Array with Jackson 2: more compact output

Writing Java POJOs out as simple JSON is a good general mechanism for serializing Java Objects, to be transferred over network: sent as input for REST services, returned as output, and sometimes stored as-is in data store.
Due to simplicity and general reliability, this output format has become de-facto default serialization format for public APIs on network system.

Challenge: verbosity of JSON

public class Point {
int x, y;
}

and its default JSON output:

{ "x" : 16, "y" : 27 }

While this is not an overly verbose example (since we are using very short property names, for example), this is not the most compact textual representation.

Improvement idea: use JSON Arrays

[ 16, 27 ]

which would work if (but only if) we can guarantee ordering, and associate position (first column, second column) with property names (x, y) unambiguously. So logically we could transform between this compact representation, and logical Object it matches.

Jackson Already Supports Doing This

Turns out that Jackson databind already supports this: in fact, has supported since version 2.1, released 4 years ago!
The key is annotation @JsonFormat and its property shape which allows altering kind of JSON value that will be produced on serialization: it may be — for example — be used to choose between numeric (timestamp) and textual (ISO-8601) serialization of Date/Time values. In this case we want to indicate that POJO that would normally be serialized as JSON Object should instead be serialized as JSON Array. For earlier example we simply add:

@JsonFormat(shape=JsonFormat.Shape.ARRAY)
public class Point {
int x, y;
}

and resulting output is changed.

Alternative ways to configure

public class Stuff {
@JsonFormat(shape=JsonFormat.Shape.ARRAY)
public Point origin;
// ... other fields
}

This can also be useful when working with 3rd party datatypes, where you can not directly add annotations (although you could use “mix-in annotations”).

Even further: with Jackson 2.8 it is also possible to define “write as array” completely without annotations, using so-called config overrides:

ObjectMapper mapper = new ObjectMapper();
mapper.configOverride(Point.class)
.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.ARRAY));

which effectively works same as using @JsonFormat annotations on class itself.

One thing to note for all of above: annotation does not work recursively — that is, if annotated type has properties, those properties are not automatically serialized as arrays, but need to be similarly configured.

Benefits

In this particular case improvement can be even bigger for one specific reason: processing of JSON property names is often more expensive (character-by-character basis) than processing of values — names need to be decoded, validated, whereas some values (like integral numbers) do not require as much work.

Still not impressed? Since Jackson supports multiple alternative data formats beyond JSON, this feature can be very helpful with “JSON-like” formats: in particular “binary JSON” formats CBOR and Smile benefit a lot from using this style of output. In some benchmarks (like jackson-benchmarks) throughput improvement is 50%, for already efficient read/write.

Limits?

Another type of limitation is that of filtering: when using @JsonView it is possible to serialize as array (as well as with @JsonFilter), but values can not be completely filtered out because positional nature of output (that is: removing of an element will shift values causing possible mismatch). As such, this output mode is not usually a good match for filtering output.

Note that as a user you do not have to enforce such limits: Jackson will only use “output as Array” in cases where it is possible. If it is not possible, default output as JSON Object will be used: similarly, when deserializing, expectation matches output side so that end-to-end functionality works.
But it is good to be aware of limitations to understand why sometimes annotations may not change output structure.

When To Use and When Not

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
@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