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

<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

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

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

@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

  • public getters and setters
  • public fields (if field-detection enabled)
  • public “is-getters” (like boolean 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:

  1. Auto-detect setter methods of any visibility type (even ones declared private)
  2. Auto-detect public, protected and “package protected” getter methors (not just public)
  3. 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

  • 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

  • @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 of jackson-databind , not jackson-annotations)

If you have specific extension ideas, wishes, requests, feel free to file an RFE at Jackson-jr Issue Tracker.

Open Source developer, most known for Jackson data processor (nee “JSON library”), author of many, many other OSS libraries for Java, from ClassMate to Woodstox