Sunday, November 17, 2013

Jackson 2.3.0 released -- quick feature overview

Now that Jackson 2.3.0 is finally finalized and released (official release date 14th November, 2013), it is time for a quick sampling of new features. Note that this is a very limited sampling -- across all core components and modules, there are close to 100 closed features; some fixes, but most improvements of some kind.

So here's my list of 6 notable features.

1. JsonPointer support for Tree Model

One of most often asked features for Jackson has been ability to support a path language to traverse JSON. So with 2.3 we chose the simplest standardized alternative, JSON Pointer (version 3), and made Tree Model (JsonNode) allow navigating using it.

Usage is simple: for JSON like


{
"address" : { "street" : "2940 5th Ave", "zip" : 980021 }, "dimensions" : [ 10.0, 20.0, 15.0 ]
}

you could use expressions like:


JsonNode root = mapper.readTree(src);
int zip =root. at("/address/zipcode").asIntValue();
double height = root.add("/dimensions/1").asDoubleValue(); // assuming it's the second number in there

Also note that you can pre-compile JSON Pointer expressions with "JsonPointer.compile(...)", instead of passing Strings; however, pointer expressions are not particularly expensive to tokenize. JsonPointer instances also have full serialization and deserialization support, so you can conveniently use them as part of configuration data for things like, say, DropWizard Configuration objects.

2. @JsonFilter improvements

With earlier versions, it was only possible to define ids of filters to apply using @JsonFilter on classes. With 2.3.0 you can apply this annotation (as well as @JsonView) on properties as well, to use different filters for different instances of same class:


public class POJO {
@JsonFilter("filterA") public Value value1;
@JsonFilter("filterB")public Value value2;

// similarly with JsonView (was added in 2.2)
@JsonView(AlternateView.class) public AnotherValue property;
}

But this is not all! Applicability of JSON Filters is also expanded, so that in addition to regular POJOs and their properties, it will now apply to:

  1. Any getters (they will get filtered just like regular properties)
  2. Java Maps

and in future we may also add ability to filter out List or array elements; this will be possible since the filtering interfaces were extended allow alternate calls that specify value index, instead of property name.

3. Uniqueness checks

Although JSON specification does not mandate enforcing uniqueness of Object property names (and use of databinder on serialization should prevent generation of duplicates), there are situations where one would want to be extra careful, and make parser check uniqueness.

With 2.3.0, there are two new features you can turn on to cause an exception to be thrown if duplicate property names are encountered:

  1. DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY: when reading JSON Trees (JsonNode), and encountering a duplicate name, throw a JsonMappingException
  2. JsonParser.Feature.STRICT_DUPLICATE_DETECTION and JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION: when reading or writing JSON using Streaming API (either directly, or for data-binding or building/serializing Tree instances), duplicates will be reported by a JsonParsingException

The main difference (beyond applicability; first feature only affects cases of building a JsonNode out of JSON input) is that duplicate detection at Streaming API level does incur some overhead (up to 30-40% more time spent); whereas duplicate detection at Tree Model level has little if any overhead. Difference is due to additional storage and checking requirements, as Streaming API does not need to keep track of set of property names encountered unless checking is required, whereas Tree Model will have to keep track of properties anyway. As a consequence, tree level checks are basically close to free to add.

4, Object Id handling

There are two improvements to Object Identity handling.

First, by enabling, SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID you can loosen checks so that all values that are deemed equal (by calling Object.equals()) are considered "same object"; basically this allows canonicalization of objects. It mostly makes sense when using ORM libraries, or other data sources that may not use exact object instances; or if you want to reconstruct shared references from sources that do not support it.

Second, when using YAML data format module, all Object Id references are now written using YAML native constructs called anchors, and references handled as native references. Same is also true for Type Ids; YAML module will now use tags for this purpose.
This change should not onlt make result more compact and "YAML-looking", but should also improve interoperability with native YAML tools. Latter should be most useful for a common use case for YAML, that of configuration files, where you can more easily share common configuration blocks with anchors and references.

5. Contextual Attributes for custom (de)serialization

One thing that has been missing so far from both SerializaterProvider and DeserializationContext objects (both of which extends DatabindContext base class) has been ability to assign and access temporary values during serialization/deserialization. In absence of something like this, custom JsonSerializer and JsonDeserializer implementations have had to use ThreadLocal to retain such values, adding more complexity.

But not any more: starting with 2.3.0, there is concept of "databind attributes" (similar to, say, Servlet attributes), managed and accessed using two simple methods -- getAttribute(Object key) and setAttribute(Object key, Object value). These can be used for keeping track of per-call (serialization or deserialization) state, pass data between (de)serializers and so on. All values are cleared when context is created (for a readValue() or writeValue() call); and similarly values are cleared at the end, so that no explicit clean up is required.

This may not sound like a huge feature in itself, but it actually opens up interesting possibilities for future: specifically, it may make sense to add new "standard attributes" that are set by databind module itself, when specific feature is enabled. For example, perhaps it would make sense to keep track of POJO that is being currently serialized/deserialized, to be accessible to actual (de)serializers.

6. Null handling for serialization

Another relative small, but often requested feature is ability to control how Java nulls are serialized, using more granular control than global rules like "all nulls to be serialized as empty Strings" (which is possible to do already). This is supported by adding a new property for @JsonSerialize annotation:


public class POJO {
@JsonSerialize(nullsUsing=MyNullSerializer.class)
public Value value;
}

in which case instance of MyNullSerializer would be used to write a JSON value for property "value", if POJO property has value null.

7. Other misc improvements

JAX-RS module has a new mechanisms for fully customizing ObjectReader and ObjectWriter instances, above and beyond what module itself can do. You can find more details on Issue#33.

XML module has been improved as well; it is finally possible to properly serialize root-level array and java.util.Collection values. ObjectMapper.convertValue() now works properly.

CSV module now supports filtering using JSON Views and JSON Filters; this was not working correctly with earlier versions.

Posted by Tatu Saloranta at Sunday, November 17, 2013 3:27 PM
Categories: Java, JSON, Open Source
| Permalink |Comments | links to this post

Monday, October 28, 2013

On Jackson: Serializing Lists, Maps with properties

1. Problem

One of questions occasionally brought up is why Jackson ignores any properties that a java.util.List or java.util.Map may have. For example, for:


public class StringList extends ArrayList<String> {
public int id;
}

the serialized version will just be a JSON array, without property "id". This is also true for Java Maps: only contents are serialized, not properties. This is because serializers (and deserializers) have special handling; and in case of Lists, there isn't even any place to add properties in.

2. Solution

But is there any way to add support for additional properties, without resorting to custom serializers and deserializers?

Yes! With Jackson 2.1 and above there is a new annotation, @JsonFormat, and specifically its "shape" property.

Like so:


@JsonFormat(shape=JsonFormat.Shape.OBJECT)
public class StringListexten ds ArrayList<String> {
public int id;

public Iterator<String> getValues() { return iterator(); }
public void setValues(Collection<String> v) { addAll(v); }
}

What does this do? It will basically instruct Jackson to forget about special handling for Collections and Maps, and instead consider them POJOs. This means that introspection is used to find logical properties to (de)serialize.

One caveat: since there is no special handling for contents, you HAVE to add getter/setter to actually include contents.

3. Output

For type like above, you would get something like:


{ "id":123, "values":["abc","xyz"]}

Similarly for Maps, you would get properties at one JSON Object level, and values (if included) as a JSON Object-valued property:


{ "id" : 345,
"value" : {
"key1":"value1",
"key2":"value2" }
}

Or, for extra credit, you can even try using "any setter" and "any getter", to create a Map with flat set of values -- this works, since Map is just like any POJO from Jackson's point of view now.

Posted by Tatu Saloranta at Monday, October 28, 2013 8:06 PM
Categories: Java, JSON
| Permalink |Comments | links to this post

Tuesday, September 10, 2013

Java Classmate 0.9 released -- getting ready for 1.0

UPDATE: (October 2013) -- Version 1.0 is now out!

----

Getting close to Java Classmate 1.0: version 0.9 now out!

Java Classmate is a highly specialized library that can be used to properly and completely resolve type information about Java generic types used in field and method signatures. This is information that is not, oddly enough, easily accessible via JDK -- if you try figure out, for example, what is actual return type for method 'IntegerProcessor.foo()' below:

  public class MyStringKeyMap<V> extends Map<String,V> { }
  public class Processor<T> {
    public T foo();
  }
  public class IntegerProcessor extends Processor<MyStringKeyMap<Integer>> { }

you are in for quite a ride. As a programmer, you should be able to figure out that it is equivalent to Map<String,Integer>. But dynamically, from your code, how would you figure it out? I won't bother you with all the complications: for more background feel free to read "Why 'java.lang.reflect.Type' does not cut it".
And my suggested solution is Java Classmate, as per "Use Java Classmate for resolving generic signatures".

Last change to test

Java Classmate library has been used by quite a few frameworks, such as Hibernate and JBoss, and considered for inclusion by others (Netty uses code adopted from JCM, to avoid adding external dependencies; Jackson uses "predecessor", and this is code from which JVM was originally built). There hasn't been much activity for past 12 months; no new bug reports, and things appear to Just Work, which is great.

So at this point version 0.9 is released, and I am hoping to get some last piece of feedback before releasing the "official" 1.0 version. Please let me know of any issues; the best way is via project's Github issue tracker.

Posted by Tatu Saloranta at Tuesday, September 10, 2013 10:48 AM
Categories: Java, Open Source
| Permalink |Comments | links to this post

Tuesday, August 13, 2013

On Jackson 2.2

Here's another thing I need to write about, from my "todo bloklog" (blog backlog): overview of Jackson 2.2 release.
As usual, official 2.2 release notes are worth checking out for more detailed listing.

1. Overview

Jackson 2.2.0 was released in April, 2013 -- that is, four months ago -- and the latest patch version currently available is 2.2.2. It has proven a nice, stable release, and is currently used by frameworks such as DropWizard (my current favorite Java service platform). This is also the current stable version, as the development for 2.3 is not yet complete.

As opposed to earlier releases (2.0 major, 2.1 minor) which overflowed with new functionality, focus with 2.2 was to really stabilize functionality and close as many open bugs as possible; especially ones related to new 2.x functionality.
Related to this, we wanted to improve parity (coverage of features to different parts; that is, that both serialization and deserialization support things; that Map/Node/POJO handling would be as similar as possible).

2. Enhancements to serializer, deserializer processing

One problem area, with respect to writing custom handlers for structured non-POJO types (esp. container types: arrays, Collections, Maps), was that BeanSerializerModifier and BeanDeserializerModifier handlers could only be used for POJO types.

But custom handling is needed for container types too; and especially so when adding support for third-party libraries like Trove, Guava and HPPC. 2.2 extended these interfaces to allow post-processing serializers/deserializers for all types (also including scalar types).

Ability to post-process (de)serializers of all types should reduce the need for writing custom (de)serializers from scratch: it is possible -- for example -- to take the default (de)serializer, and use post-processor to create (de)serializer that delegates to the standard version for certain cases, or for certain part of processing.

3. Converters

The biggest new feature was adding annotation-based support for adding things called "Converters". It can be seen as sort of further extension for the idea that one should be able to refine handling with small(er) component, instead of having to write custom handlers from scratch.

The basic idea is simple: to serialize custom types (that Jackson would not know how to handle correctly) one can write converters that know how to take a custom type and convert it into an intermediate object that Jackson already knows how to serialize. This intermediate form could be simple java.util.Map or JsonNode (tree model), or even just another more traditional POJO.

And for deserialization, do the reverse: let Jackson deserialize JSON into this intermediate type; and call converter to get to the custom type.

Typically you will write one or two converters (one if you just need converter for either serialization or deserialization; two if both); and then either annotate the type that needs converter(s); or property of that type that needs converter(s):


@JsonSerialize(converter=SerializationConverter.class)
@JsonDeserialize(converter=DeserializationConverter.class)
public class Point {
private int x, y;
public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}
public int x() { return x; }
public int y() { return y; }
}

class SerializationConverter extends StdConverter<ConvertingBean, int[]> {
public int[] convert(MyPoint p) {
return new int[] { p.x(), p.y() };
}
}
// similarly for DeserializationConverter: StdConverter is convenient base class

This feature was inspired by similar concept in JAXB, and should have been included a long time ago (actually, 2.1 already added internal support for converters; 2.2 just added annotation and connected the dots).
One thing worth noting regarding above is that use of StdConverter is strongly recommended; although you may directly implement Converter there is usually little need. Also note that although example associated converters directly with the type, you can also add them to property definition; this can be useful when dealing with third-party types (although you can also use mix-in annotations for those cases).

4. Android

One "unusual" area for improvements was work to try to make Jackson run better on Android platform. Android has its set of quirks; and although Jackson was already working well from functionality perspective, there were obvious performance problems. This was especially true for data-binding, where initial startup overhead has been problematic.

One simple improvement was elimination of file VERSION.txt. While it seemed harmless enough thing for J2SE, Android's package loader has surprising overhead when loading resources from within a jar -- at least on some versions, contents of jar are retained in memory basically DOUBLING amount of memory needed. 2.2 replaced text-file based version discovery with simple class generation (as part of build, that is, static source generation).

Version 2.2 also contained significant amount of internal refactorings, to try to reduce startup overhead, by both simplifying set up of (de)serializers, and to try to improve lazy-loading aspects.
One challenge, however, is that we still do not have a good set of benchmarks to actually verify effects of these changes. So while the intent was to improve startup performance, we do not have solid numbers to report, yet.

On plus side, there is some on-going work to do more performance measurements; and I hope to write more about these efforts once related work is made public (it is not yet; I am not driving these efforts, but have helped).

5. JAX-RS: additional caching

Another area of performance improvements was that of JAX-RS provider. Earlier versions did reuse internal `ObjectMapper`, but had to do more per-call annotation processing. 2.2 added simple caching of results of annotation introspection, and should help reduce overhead.

One other important change was structural: before 2.2, there were multiple separate github projects (three; one for JSON, another for Smile, third for XML). With 2.2 we now have a single Github project, jackson-jaxrs-providers, with multiple Maven sub-projects that share code via a base package. This should simplify development, and reduce likelihood of getting cut'n paste errors.

6. AfterBurner becomes Production Ready

One more big milestone concerned Afterburner module (what is it? Check out this earlier entry). With a little help from my friends (special thanks to Steven Schlansker for his significant contributions!), all known issues were addressed and new checks added, such that we can now consider Afterburner production ready.

Given that use of Afterburner can give you 30-50% boost in throughput, when using data-binding, it might be good time to check it out.

Posted by Tatu Saloranta at Tuesday, August 13, 2013 10:37 PM
Categories: Java, JSON, Open Source
| Permalink |Comments | links to this post

Thursday, August 08, 2013

Brief History of Jackson the JSON processor

(Disclaimer: this article talks about Jackson JSON processor -- not other Jacksons, like American cities or presidents -- those others can be found from Wikipedia)

0. Background

It occurred to me that although it is almost six years since I released the first public version of Jackson, I have not actually written much about events surrounding Jackson development -- I have written about its features, usage, and other important things. But not that much about how it came about.

Since still remember fairly well how things worked out, and have secondary archives (like this blog, Maven/SVN/Github repositories) available for fact-checking the timeline, it seems like high time to write a short(ish) historical document on the most popular OSS project I have authored.

1. Beginning: first there was Streaming

Sometime in early 2007, I was working at Amazon.com, and had successfully used XML as the underying data format for couple of web services. This was partly due to having written Woodstox, a high-performance Java XML parser. I was actually relatively content with the way things worked with XML, and had learnt to appreciate benefits of open, standard, text-based data format (including developer-debuggability, interoperability and -- when done properly -- even simplicity).
But I had also been bitten a few times by XML data-binding solutions like JAXB; and was frustrated both by complexities of some tools, and by direction that XML-centric developers were taking, focusing unnecessarily in the format (XML) itself, instead of how to solve actual development problems.

So when I happened to read about JSON data format, I immediately saw potential benefits: the main one being that since it was a Data Format -- and not a (Textual) Markup Format (like XML) -- it should be much easier to convert between JSON and (Java) objects. And if that was simpler, perhaps tools could actually do more; offer more intuitive and powerful functionality, instead of fighting with complex monsters like XML Schema or (heaven forbid) lead devs to XSLT.
Other features of JSON that were claimed as benefits, like slightly more compact size (marginally so), or better readabilty (subjective) I didn't really consider particularly impresive.
Beyond appreciating good fit of JSON for web service use case, I figured that writing a simple streaming tokenizer and generator should be easy: after all, I had spent lots of time writing low-level components necessary for tokenizing content (I started writing Woodstox in late 2003, around time Stax API was finalized)

Turns out I was right: I got a streaming parser working and in about two weeks (and generator in less than a week). In a month I had things working well enough that the library could be used for something. And then it was ready to be released ("release early, release often"); and rest is history, as they say.

Another reason for writing Jackson, which I have occasionally mentioned, was what I saw as a sorry state of JSON tools -- my personal pet peeve was use of org.json's reference implementation. While it was fine as a proof-of-concept, I consider(ed) it a toy library, too simplistic, underpowered thing for "real" work. Other alternatives just seemed to short-change one aspect or another: I was especially surprised to find total lack of modularity (streaming vs higher levels) and scant support for true data-binding -- solutions tended to either assume unusual conventions or require lots of seemingly unnecessary code to be written. If I am to write code, I'd rather do it via efficient streaming interface; or if not, get a powerful and convenient data-binding. Not a half-assed XML-influenced tree model, which was en vogue (and sadly, often still is).

And the last thing regarding ancient history: the name. I actually do not remember story behind it -- obviously it is a play on JSON. And I vaguely recall toying with the idea of calling library "Jason", but deciding that might sound too creepy (I knew a few Jasons, and didn't want confusion). Compared to Woodstox -- where I actually remember that my friend Kirk P gave the idea (related to Snoopy's friend, bird named Woodstock!) -- I actually don't really know who to give credit to the idea, or inspiration to it.

2. With a FAST Streaming library...

Having written (and quickly published in August 2007) streaming-only version of Jackson, I spent some time optimizing and measuring things, as well as writing some code to see how convenient library is to use. But my initial thinking was to wrap things up relatively soon, and "let Someone Else write the Important Pieces". And by "important pieces" I mostly meant a data-binding layer; something like what JAXB and XMLBeans are to XML Streaming components (SAX/Stax).

The main reasons for my hesitation were two-fold: I thought that

  1. writing a data-binding library will be lots of work, even if JSON lends itself much more easily to doing that; and
  2. to do binding efficiently, I would have to use code-generation; Reflection API was "known" to be unbearably slow

Turns out that I was 50% right: data-binding has consumed vast majority of time I have spent with Jackson. But I was largely wrong with respect to Reflection. But more on that in a bit.

In short term (during summer and autumn of 2008) I did write "simple" data-binding, to bind Java Lists and Maps to/from token streams; and I also wrote a simple Tree Model, latter of which has been rewritten since then.

3. ... but No One Built It, So I did

Jackson the library did get relatively high level of publicity from early on. This was mostly due to my earlier work on Woodstox, and its adoption by all major second-generation Java SOAP stacks (CXF nee XFire; Axis 2). Given my reputation for producing fast parsers, generators, there was interest in using what I had written for JSON. But early adopters used things as is; and no one did (to my knowledge) try to build higher-level abstractions that I eagerly wanted to be written.

But that alone might not have been enough to push me to try my luck writing data-binding. What was needed was a development that made me irritated enough to dive in deep... and sure enough, something did emerge.

So what was the trigger? It was the idea of using XML APIs to process JSON (that is, use adapters to expose JSON content as if it was XML). While most developers who wrote such tools consider this to be a stop-gap solution to ease transition, many developers did not seem to know this.
I thought (and still think) that this is an OBVIOUSLY bad idea; and initially did not spend much time refuting merits of the idea -- why bother, as anyone should see the problem? I assumed that any sane Java developer would obviously see that "Format Impedance" -- difference between JSON's Object (or Frame) structure and XML Hierarchic model -- is a major obstacle, and would render use of JSON even MORE CUMBERSOME than using XML.

And yet I saw people suggesting use of tools like Jettison (JSON via Stax API), even integrating this into otherwise good frameworks (JAX-RS like Jersey). Madness!

Given that developers appeared intent ruining the good thing, I figured I need to show the Better Way; just talking about that would not be enough.
So, late in 2008, around time I moved on from Amazon, I started working on a first-class Java/JSON data-binding solution. This can be thought of as "real" start of Jackson as we know it today; bit over one year after the first release.

4. Start data-binding by writing Serialization side

The first Jackson version to contain real data-binding was 0.9.5, released December of 2008. Realizing that this was going to be a big undertaking, I first focused on simpler problem of serializing POJOs as JSON (that is, taking values of Java objects, writing equivalent JSON output).
Also, to make it likely that I actually complete the task, I decided to simply use Reflection "at first"; performance should really matter only once thing actually works. Besides, this way I would have some idea as to magnitude of the overhead: having written a fair bit of manual JSON handling code, it would be easy to compare performance of hand-written, and fully automated data-binder.

I think serializer took about a month to work to some degree, and a week or two to weed out bugs. The biggest surprise to me was that Reflection overhead actually was NOT all that big -- it seemed to add maybe 30-40% time; some of which might be due to other overhead beside Reflection access (Reflection is just used for dynamically calling get-methods or accessing field values). This was such a non-issue for the longest time, that it took multiple years for me to go back to the idea of generating accessor code (for curious, Afterburner Module is the extension that finally does this).

My decision to start with Serialization (without considering the other direction, deserialization) was good one for the project, I believe, but it did have one longer-term downside: much of the code between two parts was disjoint. Partly this was due to my then view that there are many use cases where only one side is needed -- for example, Java service only every writing JSON output, but not necessarily reading (simple query parameters and URL path go a long way). But big part was that I did not want to slow down writing of serialization by having to also consider challenges in deserialization.
And finally, I had some bad memories from JAXB, where requirements to have both getters AND setters was occasionally a pain-in-the-buttocks, for write-only use cases. I did not want to repeat mistakes of others.

Perhaps the biggest practical result of almost complete isolation between serialization and deserialization side was that sometimes annotations needed to be added in multiple places; like indicating both setter and getter what the JSON property name should be. Over time I realized that this was not a good things; but the problem itself was only resolved in Jackson 1.9, much later.

5. And wrap it up with Deserialization

After serialization (and resulting 0.9.5) release, I continued work with deserialization, and perhaps surprisingly finished it slightly faster than serialization. Or perhaps it is not that surprising; even without working on deserialization concepts earlier, I had nonetheless tackled many of issues I would need to solve, including that of using Reflection efficiently and conveniently; and that of resolving generic types (which is a hideously tricky problem in Java, as readers of my blog should know by now).

Result of this was 0.9.6 release in January 2009.

6. And then on to Writing Documentation

After managing to get the first fully functional version of data-binding available, I realized that the next blocker would be lack of documentation. So far I had blogged occasionally about Jackson usage; but for the most part I had relied on resourcefulness of the early adopters, those hard-working hardy pioneers of development. But if Jackson was to become the King of JSON on Java platform, I would need to do more for it users.

Looking blog at my blog archive I can see that some of the most important and most read articles on the site are from January of 2009. Beyond the obvious introductions to various operating modes (like "Method 2, Data Binding"), I am especially proud of "There are Three Ways to Process Json!" -- an article that I think is still relevant. And something I wish every Java JSON developer would read, even if they didn't necessarily agree with all of it. I am surprised how many developers blindly assume that one particular view -- often the Tree Model -- is the only mode in existence.

7. Trailblazing: finally getting to add Advanced Features

Up until version 1.0 (released May 2009), I don't consider my work to be particularly new or innovative: I was using good ideas from past implementations and my experience in building better parsers, generators, tree models and data binders. I felt Jackson was ahead of competition in both XML and JSON space; but perhaps the only truly advanced thing was that of generic type resolution, and even there, I had more to learn yet (eventually I wrote Java ClassMate, which I consider the first Java library to actually get generic type resolution right -- more so than Jackson itself).

This lack of truly new, advanced (from my point of view) features was mostly since there was so much to do, all the foundational code, implementing all basic and intermediate things that were (or should have been) expected from a Java data-binding library. I did have ideas, but in many cases had postponed those until I felt I had time to spare on "nice-to-have" things, or features that were more speculative and might not even work; either functionally, or with respect to developers finding them useful.

So at this point, I figured I would have the luxury of aiming higher; not just making a bit Better Mousetrap, but something that is... Something Else altogether. And with following 1.x versions, I started implementing things that I consider somewhat advanced, pushing the envelope a bit. I could talk or write for hours on various features; what follows is just a sampling. For slightly longer take, read my earlier "7 Killer Features of Jackson".

7.1 Support for JAXB annotations

With Jackson 1.1, I also started considering interoperability. And although I thought that compatibility with XML is a Bad Idea, when done at API level, I thought that certain aspects could be useful: specifically, ability to use (a subset of) JAXB annotations for customizing data-binding.

Since I did not think that JAXB annotations could suffice alone to cover all configuration needs, I had to figure a way for JAXB and Jackson annotations to co-exist. The result is concept of "Annotation Introspector", and it is something I am actually proud of: even if supporting JAXB annotations has been lots of work, and caused various frustrations (mostly as JAXB is XML-specific, and some concepts do not translate well), I think the mechanism used for isolating annotation access from rest of the code has worked very well. It is one area that I managed to design right the first time.

It is also worth mentioning that beyond ability to use alternative "annotation sets", Jackson's annotation handling logic has always been relatively advanced: for example, whereas standard JDK annotation handling does not support overriding (that is; annotations are not "inherited" from overridden methods), Jackson supports inheritance of Class, Method and even Constructor annotations. This has proven like a good decision, even if implementing it for 1.0 was lots of work.

7.2 Mix-in annotations

One of challenges with Java Annotations is the fact that one has to be able to modify classes that are annotated. Beyond requiring actual access to sources, this can also add unnecessary and unwanted dependencies from value classes to annotations; and in case of Jackson, these dependencies are in wrong direction, from design perspective.

But what if one could just loosely associate annotations, instead of having to forcible add them in classes? This was the thought exercise I had; and led to what I think was the first implementation in Java of "mix-in annotations". I am happy that 4 years since introduction (they were added in Jackson 1.2), mix-in annotations are one of most loved Jackson features; and something that I still consider innovative.

7.3 Polymorphic type support

One feature that I was hoping to avoid having to implement (kind of similar, in that sense, to data-binding itself) was support for one of core Object Serialization concepts (but not necessarily data-binding concept; data is not polymorphic, classes are): that of type metadata.
What I mean here is that given a single static (declared) type, one will still be able to deserialize instances of multiple types. The challenge is that when serializing things there is no problem -- type is available from instance being serialized -- but to deserialize properly, additional information is needed.

There are multiple problems in trying to support this with JSON: starting with obvious problem of JSON not having separation of data and metadata (with XML, for example, it is easy to "hide" metadata as attributes). But beyond this question, there are various alternatives for type identifiers (logical name or physical Java class?), as well as alternative

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.