Saturday, August 03, 2013

Jackson 2.1 was released... quite a while ago :)

Ok, so I have not been an active blogger for a while. Like, since about a year ago. I am hoping to catch up a bit, so let's start with intermediate Jackson releases that have gone out the door since I last wrote about Jackson.

1. Jackson 2.1

Version 2.1 was released almost a year ago, October 2012. After big bang of 2.0 release -- what with all the crazy new features like Object Id handling (for fully cyclic object graphs), 2.1 was expected to be more minor release in every way.

But, that was not to be... instead, 2.1 packed an impressive set of improvements of its own.
Focus was on general usability: improved ergonomics, bit of performance improvements (for data-binding) and the usual array of bug fixes that required bigger changes in internals (and occasionally additional API) than what can be done in a patch release.

For more complete handling of what exactly was added, you can check out my Jackson 2.1 Overview presentation I gave at Wordnik (thanks Tony and folks!). Note that links to this and other presentations can be found from Jackson Docs github repo.
For full list of changes, check 2.1 Release Notes.

But here's a Reader's Digest version.

2. Shape-shifting

@JsonFormat annotation was added in Jackson 2.0, but was not used by many datatypes. With 2.1, there are interesting (and back then, experimental; but it is much more stable now!) new features to let you change the "shape" (JSON Structure) of some of common Java datatypes:

  • Serialize Enums as JSON Objects instead of Strings: useful for serialization, but can not deserialize back (how would that work? Enums are singletons)
  • Collections (Sets, Lists) as JSON Objects (instead of arrays): useful for custom Collections that add extra properties -- can also deserialize, with proper use of @JsonCreator annotations (or custom deserializer)
  • POJOs as Arrays! Instead of having name/value pairs, you will get JSON arrays where position indicates which property is being used (make sure to use @JsonPropertyOrder annotation to define ordering!)

Of these, the last option is probably the most interesting. It can make JSON as compact as CSV; and in fact can compete with binary formats in many cases, especially if values are mostly Strings.
A simple example would be:

  public class Point {
    public int x, y;

which, when serialized, could look like:

  [ 1, 2]

instead of earlier

  { "x":1, "y":2 }

and obviously works for reading as well (that is, you can read such tabular data back).

3. Chunked (partial) Binary Data reads, writes

When dealing with really large data, granularity of JsonParser and JsonGenerator works well, except for case of long JSON Strings; for example, ones that contain Base64-encoded data. Since these values may be potentially very large, and since they are quite often just stored on disk (or read from disk to send) -- and there is no benefit from keeping the whole value in memory at all -- it makes sense to offer some way to allow streaming for values, not just between values.

To this end, JsonParser and JsonGenerator now do have methods that allow one to read and write large binary data chunks without retaining more than a limited amount of data in memory (one buffer-full, like 8 or 16kB) at any given point. Access is provided via and, with methods:

JsonGenerator.writeBinary(InputStream, int expectedLength)

Note that while direction of arguments may look odd, it actually makes sense when you try using it: you will provide handler for content (which implements OutputStream), and source for content to write (InputStream).

4. Format auto-detection support for data-binding

Another innovative new feature is ability to use already existing data format auto-detection, without having to use Streaming API. Earlier versions included support for JsonParser auto-detecting type of input, for data formats that support this (some binary formats do not; I consider this a flaw in such formats; of text formats, CSV does not): at least JSON, XML, Smile and YAML support auto-detection.

You enable support through ObjectReader for example like so:

  ObjectMapper mapper = new ObjectMapper();
  XmlMapper xmlMapper = new XmlMapper(); // XML is special: must start with its own mapper
  ObjectReader reader = mapper
    .reader(POJO.class) // for reading instances of POJO
    .withFormatDetection(new JsonFactory(), xmlMapper.getFactory(), new SmileFactory();

and then you can use resulting reader normally:

  User user = mapper.readValue(new File("input.raw"), User.class);

and input that is in XML, JSON or Smile format will be property decoded, and bound to resulting class. I personally use this to support transparent usage of Smile (binary JSON) format as a pluggable optimization over JSON.

5. Much improved XML module

Although XML module has existed since earlier 1.x versions, 2.0 provided first solid version. But it did not include support for one commonly used JAXB feature: ability to use so-called "unwrapped" Lists. 2.1 fixes this and fully supports both wrapped and unwrapped Lists.

But beyond this feature, testing was significantly extended, and a few specific bugs were fixed. As a result version 2.1 is the first version that I can fully recommend as replacement for JAXB processing in production environments.

6. Delegating serializer, deserializer

Final new feature is support for so-called delegating serializers and deserializers. The basic idea is simple: instead of having to build fully custom handlers you only need to implement converters that can convert your custom types into something that Jackson can automatically handle (supports out of the box).

Details of this are included in 2.1 presentation; most commonly you will just extend com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer and com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer.

blog comments powered by Disqus

Sponsored By

Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me
Check my profile to learn more.