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

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store