Using Jackson annotations with Jackson-jr
(to rename and remove properties)
Something that I briefly covered earlier (in Jackson 2.11 features) is the new jackson-jr extension called jackson-jr-annotation-support
. But as I have not gotten much feedback since that release, maybe it is time to re-review this additional functionality.
(note: if you are not familiar with Jackson-jr library itself, you may want to read “Jackson-jr for ‘casual JSON’” first before continuing)
Introduction of general “extension” mechanism for jackson-jr — not unlike full Jackson’s “modules” — coincided with the addition of first such extension, “jackson-jr-annotation-support”, which offers optional support for some of basic Jackson annotations for basic detection (and exclusion) of properties; renaming, aliasing, reordering (for output).
Enabling Jackson-annotations Extension for Jackson-jr
To use this extensions you will need to add a dependency in your build file; with Maven you would add:
<dependency>
<groupId>com.fasterxml.jackson.jr</groupId>
<artifactId>jackson-jr-annotation-support</artifactId>
<version>2.12.0</version>
</dependency>
and then you will need to register it with the JSON
instance you use:
JSON j = JSON.builder().register(JacksonAnnotationExtension.std)
// add other configuration, if any
.build();
after doing this, support would be enabled for following annotations:
@JsonProperty
for basic inclusion, renaming@JsonPropertyOrder
for defining specific order of properties when writing@JsonIgnore
/@JsonIgnoreProperties
for ignoring specified visible properties@JsonAlias
for specifying alternate names to accept when reading@JsonAutoDetect
for changing default visibility rules for methods (can ignore public getters/setters and force use of annotations) and fields (can make non-public fields visible)
So let’s have a look at some common usage patterns.
Renaming fields, adding aliases
Perhaps the most common use case for annotations is that of renaming properties after auto-detection. Consider, for example, following case:
public class NameSimple {
@JsonProperty("firstName")
@JsonAlias({ "fn" })
protected String _first;
@JsonProperty("lastName")
protected String _last;
protected NameSimple() { }
public NameSimple(String f, String l) {
_first = f;
_last = l;
}
}
in this case we would both indicate use of respective fields to access 2 logical properties (instead of having to add getters and setters) and also rename them, so that compatible JSON would be like
{ "firstName":"Bob", "lastName":"Burger" }
or, considering additional alias we also specified, possibly:
{ "fn":"Bob", "lastName":"Burger" }
(but always serialized with “firstName”: alias only considered during deserialization)
As with regular Jackson, renaming only needs to be done by annotating one of accessors, for example:
public class ValueHolder {
// not needed for setting or getting, name does not matter
private int v; @JsonProperty("value")
public int getVal() { return v; }
// no need to repeat, gets renamed as well
public void setVal(int v) { this.v = v; }
}
Ignoring accessors
Another common use for annotations is to ignore property otherwise implied by existence of a getter or a setter. For example, consider this class, where by default we would see metadata
property, but that is not meant to be serialized:
public class Point
public int x;
public int y;
protected XY() { }
public XY(int x, int y) {
this.x = x;
this.y = y;
} // diagnostics for logging, not wanted for serialization
@JsonIgnore
public Metadata getMetadata() {
return calculateMetadata();
}
}
alternative we may sometimes want to use an alternative, @JsonIgnoreProperties
, especially if ignoring properties from the superclass:
// parent class has "getMetadata()", let's ignore:
@JsonIgnoreProperties({ "metadata" })
public class Point extends ValueWithMetadata
{
public int x, y;
}
Changing serialization order of properties
By default, Jackson-jr serializes properties in alphabetic order, but sometimes you may want to use different ordering. If so,
@JsonPropertyOrder({ "x", "y", "width", "height" })
public class Rectangle {
public int x, y;
public int width, height;
}
(note: there is no way to try to force “declaration order” — JDK does not guarantee such an ordering is available via Reflection, even for a single class, and trying to combine ordering across fields and methods, super/subclasses would make this futile exercise even if it di)
Changing auto-detection settings
By default, Jackson-jr auto-detects properties based on finding:
public
getters and setterspublic
fields (if field-detection enabled)public
“is-getters” (likeboolean isEnabled()
— if is-getter detection enabled)
but sometimes you might want to either prevent auto-detection altogether for certain kinds of accessors, or alternatively auto-detect accessors with lower visibility. You can use Jackson class annotation @JsonAutoDetect
for this purpose. Declaration like this, for example:
@JsonAutoDetect(
setterVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC,
fieldVisibility = JsonAutoDetect.Visibility.NONE
)
public class MyValue {
}
would:
- Auto-detect setter methods of any visibility type (even ones declared
private
) - Auto-detect
public
,protected
and “package protected” getter methors (not justpublic
) - Not auto-detect any fields, no matter what visibility (not even
public
)
this would reduce the need for per-accessor annotations or having to change accessor visibility levels. Auto-detection may still be overridden by any explicit annotations like @JsonProperty
(to include, regardless of visibility) or @JsonIgnore
(ignore regardless of visibility).
In addition to per-class annotation, you may also override the default visibility used by Jackson-jr: this is done when building extension itself, before registering it.
For example:
// start with default visibility configuration
JsonAutoDetect.Visibility vis =
JacksonAnnotationExtension.DEFAULT_VISIBILITY;
// change field-visibility:
vis = vis.withFieldVisibility(
JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC));// and then build the JSON instance with extension
final JSON aj = JSON.builder()
.register(JacksonAnnotationExtension.builder()
.withVisibility(vis)
.build())
.build();
would use defaults otherwise, but change field auto-detection to allow detection of protected
(and “package protected”) fields in addition to public
ones. These defaults may be overridden by per-class @JsonAutoDetect
settings, which in turn may be overridden by per-accessor annotations.
Limitations
Aside from only supporting a small subset of all Jackson annotations, there are some limitations regarding annotations that are supported:
- No mix-in annotation support
- Class annotations: only super-class annotations are inherited, super-interface annotations are not
- Accessor annotations: no “accessor inheritance” (fields, methods) — when overriding methods, annotations from accessor in base class will not be found
- Some annotations are only supported for classes, but not on accessors (
@JsonIgnoreProperties
) - Only renaming aspect of
@JsonProperty
is supported
Possible future additions
Although there is no plan to support all or even most Jackson annotations (if you want that, consider using “full” Jackson), support may be gradually extended in some areas based on feedback.
Some possible areas of improvement are:
@JsonValue
- Consider supporting mix-in annotations
- Maybe a subset of
@JsonCreator
functionality (property-based, explicit) should be supported - Naming strategies? (existing
@JsonNaming
cannot be used, unfortunately, as it is part ofjackson-databind
, notjackson-annotations
)
If you have specific extension ideas, wishes, requests, feel free to file an RFE at Jackson-jr Issue Tracker.