Monday, July 04, 2011

Jackson Annotations: @JsonCreator demystified

One of more powerful features of Jackson is its ability to use arbitrary constructors for creating POJO instances, by indicating constructor to use with @JsonCreator annotation. A simple explanation of this feature can be found from FasterXML wiki; but it only scratches surface of all the power this annotation exposes. This article will expand on what can be done with this annotation.

1. What are Creators in Jackson?

"Creator" refers to two kinds of things: constructors, and static factory methods: both can be used to construct new instances. Term "creator" is used for convenience, to avoid repeating "constructor or static factory method".

In some places term "creator method" may be used, although this is not preferred (since constructors are not regular methods). In case of static factory methods, method's return type must be same as declaring classes type, or its subtype.

2. Two kinds of creators: property-based, delegate-based

There are two kinds of creators that one can denote using @JsonCreator: property- and delegate-based creators:

  • Property-based creators take one or more arguments; all of which MUST be annotated with @JsonProperty, to specify JSON name used for property. They can only be used to bind data from JSON Objects; and each parameter represents one property of the JSON Object; type of property being used for binding data to be passed as that parameter when calling creator.
  • Delegate-based creators take just one argument, which is NOT annotated with @JsonProperty. Type of that property is used by Jackson to bind the whole JSON value (JSON Object, array or scalar value), to be passed as value of that one argument.

In addition, sometimes distinction is made between two kinds of delegate-based creators: those that take scalar value (int/Integer/long/Long, string or boolean/Boolean) and other delegates. There is only one important distinction from user perspective: there is no creator overloading, so only one creator of each type is alllowed. More on this later on.

3. Property-based creators

Property-based creators are typically used to pass one or more obligatory parameters into constructor (either directly or via factory method). If a property is not found from JSON, null is passed instead (or, in case of primitives, so-called default value; 0 for ints and so on).

A typical use case could look like this:


  public class NonDefaultBean {
    private final String name;
    private final int age;

    private String type;
  
    @JsonCreator
    public NonDefaultBean(@JsonProperty("name") String name, @JsonProperty("age") int age)
    {
      this.name = name;
      this.age = age;
    }

    public void setType(String type) {
      this.type = type;
    }
  }

where two properties are passed via constructor creator; and then a third one through regular setter. Note that we could have as well used a static factory method as creator.

One thing to note about these creators is that it is possible to create a sub-type of class: this allows implementing polymorphic handling manually if necessary, although with limitation that creator itself must be in base class. But all other properties can be passed to sub-classes, as Jackson is smart enough to check type and properties of the actual instance, not just declared nominal type.

4. Delegate-based creators

Whereas you can use multiple arguments with properties-based creators (but must explicitly name them), delegate-based creator takes just one argument. Jackson will then use type of that argument for data-binding, and bind JSON to that type before calling creator. Creator is then free to construct instance any way it wants to. A typical usage looks like:


  public class ObjectDelegateBean
  {
    private final String name;
    private final int age;
    private final String type;

    @JsonCreator
    public ObjectDelegateBean(Map<String,Object> props)
    {
      name = (String) props.get("name");
      age = (Integer) props.get("age");
      type = (String) props.get("type");
    }
  }  

In this case we just manually extract properties from a Map (where JSON data has been bound to "natural" types; Maps, Lists, String, Numbers and Booleans). Other common delegate types used are JsonNode (to use JSON tree as intermediate form), TokenBuffer (stream of exact JsonTokens, which can be used to create a JsonParser) and basic java.lang.Object (which would map to natural types mentioned earlier).

But as I mentioned earlier, it is also possible to use variants to handle scalar types: specifically ints (or Integers), long (or Longs) and Strings (in future support will also be added for double/Double).

For example:


  public class DateBean
  {
    private Date date;

    private DateBean(Date date) {
      this.date = date;
    }

    @JsonCreator
    public static DateBean factory(long timestamp) {
      Date d = new Date(timestamp);
      return new DateBean(d);
    }
}

which is bit contrived example, as Jackson can easily bind java.util.Date directly. However, some JSON formats support overloading, or try to minimize size; and JSON Strings are sometimes used to bundle semi-structured data

One point to note about "scalar delegates" is that Jackson allows overload of creators, as long as there is only one creator type per JSON type: so you can have only one properties-based creator (or delegate-based creator that takes something other than match of JSON scalar type) , but there can be an additional string-delegate creator as well as an int-delegate creator. Limitation is conceptually simple: there must only be one applicable delegate to choose from, knowing type of JSON value being bound -- and this is why delegates that take String, Integer/int or Long/long are special cases of delegate-based creators.

5. More to know?

In near future (Jackson 1.9) there will be significant improvements in this area: new things called "value instantiators" will be allowed to handle feature set similar to that of @JsonCreator; but that need not be bound to existing Java constructors or factory methods.

blog comments powered by Disqus

Sponsored By


Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me at@yahoo.com
Check my profile to learn more.