Feature: Mix-in Annotations
(see Jira entry http://jira.codehaus.org/browse/JACKSON-76 for details)
Before version 1.2 configuring serialization could only be done using one of 2 ways:
Modify bean classes by adding annotations, OR
- Writing custom serializers/deserializers
And for third-party classes (or classes that should not have any dependency to Jackson package, including annotation definitions), only second option was available. But that means significant additional work -- wouldn't it be nice to be able to use annotations for configuration, without having to embed them within code?
Indeed! And this is exactly what "mix-in annotations are": a way to associate annotations with classes, without modifying (target) classes themselves.
That is, you can:
Define that annotations of a mix-in class (or interface)
will be used with a target class (or interface) such that it appears
as if the target class had all annotations that the mix-in class has (for purposes of configuring serialization / deserialization)
You can think of it as kind of aspect-oriented way of adding more annotations during runtime, to augment statically defined ones.
How does it work?
You just configure ObjectMapper with associations between mix-in and target classes, to be used for serialization and deserialization.
The easiest way to do this (assuming you use Jackson 1.7 or above) is via Module interface: if you extend SimpleModule, you can do:
1 public class MyModule extends SimpleModule
2 {
3 public MyModule() {
4 super("ModuleName", new Version(0,0,1,null));
5 }
6 @Override
7 public void setupModule(SetupContext context)
8 {
9 context.setMixInAnnotations(Target.class, MixIn.class);
10 // and other set up, if any
11 }
12 }
which will register mix-ins with both serializer and deserialization configurations.
Or, if using an earlier version, you do this by:
objectMapper.getSerializationConfig().addMixInAnnotations(Target.class, MixIn.class); objectMapper.getDeserializationConfig().addMixInAnnotations(Target.class, MixIn.class);
Example use case
Let us consider an example, where you have an existing class ("target class") Point.class, defined as:
public final class Rectangle {
final private int w, h;
public Rectangle(int w, int h) {
this.w = w;
this.h = h;
}
public int getW() { return w; }
public int getH() { return h; }
public int getSize() { return w * h; }
} which would serialize, but not quite as you would want since:
- Names "w" and "h" seem silly -- why not "width" and "height"?
- There is no need to serialize "size", as it can be computed from width and height; so it should be suppressed
or deserialize, because:
- There are no setters to use, just a 2-argument constructor -- it can be used with Jackson 1.2, but needs annotations
Mix-ins to the rescue!
So, here's one possible mix-in class that can be used to inject ("mix in") annotations we would want:
abstract class MixIn {
MixIn(@JsonProperty("width") int w, @JsonProperty("height") int h) { }
// note: could alternatively annotate fields "w" and "h" as well -- if so, would need to @JsonIgnore getters
@JsonProperty("width") abstract int getW(); // rename property
@JsonProperty("height") abstract int getH(); // rename property
@JsonIgnore int getSize(); // we don't need it!
}and to configure our ObjectMapper we'd use:
objectMapper.getSerializationConfig().addMixInAnnotations(Rectangle.class, MixIn.class); objectMapper.getDeserializationConfig().addMixInAnnotations(Rectangle.class, MixIn.class);
Usage notes
Here are some notes on what kinds of annotations can be used, and for what purpose:
All annotation sets that Jackson recognizes (core annotations, JAXB extensions) can be mixed in
- All kinds of annotations (member method, static method, field, constructor annotations) can be mixed in
- Only method (and field) name and signature are used for matching annotations: access definitions (private, protected, ...) and method implementations are ignored
- hint: since method implementations are ignored, it often makes sense to define mix-ins as interfaces or abstract classes
- hint: if you can, it often makes sense to define mix-in class as a sub-class of target class, and use @Override JDK annotation to ensure method name and signature match
- Mix-ins work as expected within inheritance hierarchy: it is feasible (and useful) to attach mix-in annotations to super-classes -- if so, mix-in annotations can further be overridden by annotations sub-classes (of target) provide.
External Documentation
Here is some related documentation:
