Here's something that has been requested by multiple readers: an article
detailing how to use Jackson to support specific usage patterns.
"Jackson usage patterns" if you will.
And first topic I
want to cover is.... Immutable Objects.
1. Immutable Objects: what, why?
Immutable objects in Java are objects that are either stateless
(behaviour-only) or where state is set at construction and can not be
changed.
For example, here is a simple immutable object class:
public class Point {
private final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y;
}
The main difference to more common approach is that there are no
setters; instead, values of x and y are assigned just once in
constructor. That is actually a big difference, and makes this a better
approach for many use cases.
But why is it better? One side benefit is just simplicity; fewer methods
to write (and maintain), less code, is generally better. But the main
benefit is that immutable objects are thread-safe requiring no
synchronization: instances can be freely shared and reused between
different threads. Immutable objects are also usable as Map keys (in
fact Map keys are required to be immutable; or at least never modified
while used as a key). This allows very efficient reuse using fly-weight
pattern and canonical objects. In fact, unless mutability is
specifically needed I try to keep objects immutable, as sort of baseline.
There are also challenges: inability to change state complicates many
things. But it is worth noting that immutability only affects instance;
and system-level changing of things is quite easy. A common pattern is
that of defining factory methods like:
public class Point {
// ...
public Point move(int byX, int byY) {
return new Point(x+byX, y+byY);
}
}
so that instances are never modified; instead, new instances are
constructed based on previous state and delta of some sort. This works
nicely for many cases, and is also basis for many lock-free data
structures.
But enough about object immutability itself. What is more interesting is
if and how one can support such use cases with Jackson.
2. Can Jackson do it?
Initially it might seem unlikely that Jackson can work with immutable
objects: after all, aren't Beans required to have getters and setters?
"Classic" Java Beans do indeed need setters (mutators). And as a
consequence many data binding frameworks (JAXB) require setters, or
access to public fields.
But Jackson is neither limited to Beans -- it can work with many other
kinds of POJOs -- nor does it require setters for injecting data in. And
unlike many serialization frameworks, Jackson does not require access to
object fields either (although it can certainly use them when
instructed).
As with most Jackson functionality, there are multiple ways to achieve
immutability. Let's have a look at them one by one.
3. Simple Immutable Objects using Creator methods
The simplest way to make Jackson produce immutable objects is to
instruct it to do what Point class was already doing: make Jackson use
constructor to feed in data. This is similar to how Dependency Injection
frameworks (like Guice do), and requires use of simple annotations
(mix-in annotations work fine):
public class Point {
private final int x, y;
@JsonCreator
public Point(@JsonProperty("x") int x, @JsonProperty("y") int y) {
this.x = x;
this.y = y;
}
}
and with this, Jackson will use constructor for passing logical
properties "x" and "y", instead of trying to use fields or set methods.
The only difference to "regular" properties is that @JsonProperty is not
optional for constructor arguments, because bytecode does not store
names of parameters and they must be explicitly specified. As with other
properties one can configure different aspects, including custom
deserializer to use if any, specific subtype to use and so on.
Alternatively one can also define factory methods (constructors and
factory methods are collectively referred to as "Creator methods" in
Jackson documentation), like so:
public class Point {
@JsonFactory
public static Point makeAPoint(@JsonProperty("x") int x, @JsonProperty("y") int y) {
return new Point(x, y);
}
}
Simple? Note too that you can use Creator methods with other kinds of
POJOs. You can also mix and match field access and setter access; if so,
Creators are always called first to instantiate an Object, and setters
and fields are used afterwards. So you don't have to go all immutable;
it is often beneficial to just make certain fields immutable.
4. Faking Immutability
One thing that I did not yet mention is that there is difference between
complete immutability (as shown above), and just perceived immutability.
By latter I mean that there is no way to change state of the object form
outside (except via Reflection, more on this in a minute) -- if you can
not change the state, it is as good as immutable. Consider example where
fields "x" and "y" were not marked as final: fields are still private,
and therefore inaccessible from outside. But they can still be modified
via reflection. Since we are not considering security implications here,
such bean would be about as good (with respect to benefits of
immutability) as one were fields were forced final.
With Jackson you could actually use such fields as well; by having Point
class like this:
public class Point {
@JsonProperty private int x;
private int y;
public Point() { }
@JsonProperty private void setY(int y) { this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
}
So during deserialization, Jackson would directly access field "x"
(using Reflection) and call method "setY" to set value of "y". But since
regular code can not modify things further, object would work just as
truly immutable one does. This can lead to even simpler code if you just
annotate private fields so there is no need to copy values in
constructor.
5. Immutabilty by interface / implementation separation
Yet another possibility to achieve perceived immutability is to separate
interface of an object into multiple pieces: public read-only
(immutable) part that is exposed to application, and mutable part that
is used for actual data binding. In our case we could have basic Point
interface, implemented by PointImpl class; latter with necessary fields
and possibly setters; and interface just having accessors (getters). So
as long as application code only deals with interfaces we achieve
immutability from design perspective.
But the problem here is that of dependencies; consider a POJO like:
public class Circle {
public Point center;
public int radius;
}
how would Jackson know that 'center' instance really needs to be of type
PointImpl, as Point is just an interface (and can not be created)?
Simplest way is to use annotation @JsonDeserialize, which can define
actual (sub)type to use (as well as custom deserializer to use; or for
Collection and Map types, actual types of values and map keys, and
custom deserializers for them too!):
public class Circle {
@JsonDeserialize(as=PointImpl.class)
public Point center;
public int radius;
}
and things would work as expected (alternative would be custom
deserializer which is bit more work) -- 'center' would always be an
instance of PointImpl.
6. Even crazier stuff: Materialize it!
Ok: when I said that one can not just create an instance of an
interface, I omitted something cool that Jackson 1.6 will bring: ability
to "materialize interfaces". So even though 1.6 has not yet been
released (it will be shortly, by end of August!), let's have a sneak
peek at this cool feature.
Actually, there is really very little to say, except that instead of
having to define PointImpl, one could just register bean materializer
(see this
piece of documentation for details):
mapper.getDeserializationConfig().setAbstractTypeResolver(new
AbstractTypeMaterializer());
and then Jackson would create an implementation class for any
non-concrete Bean (abstract class or interface where all abstract
methods are getters or setters) type and use it for deserialization.
Pretty cool eh?
This is actually also another fakin' it method: materialized class
actually has appropriate access (setters by default), but those are only
visible to Jackson itself (or other code that uses Introspection to find
methods).
7. More?
These are ways I can think of to make Jackson construct immutable
objects. If you can think of others, let me know. :-)
But beyond this, I have some ideas of topics to write about, like
-
How to filter out stuff to serialize (JSON Views, @JsonIgnore)
-
Working with Polymorphic types (@JsonSerialize(as=...), @JsonTypeInfo,
default typing, ...)
-
Working with Open Content (models)
And if/when you can think of other Jackson Usage Patterns, please let me
know! I'm sure there are more than 3 areas of usage that could use some
more documentation.