Jackson 2.12 Most Wanted (5/5):
Support ‘java.lang.Record’
(part 5 of “Deeper Dive on Jackson 2.12” mini-series — see “Jackson 2.12 Features” for context)
And now the last “Most Wanted” feature: something requested quite recently (less than year ago) — databind#2709: support brand new java.lang.Record
type, introduced in Java 14. Its implementation is a good example of collaboration (see 2.12 Acknowledgments), and the feature has generated a lot of excitement even before getting released.
Basic idea is pretty simple: Record
values should behave very much like a POJO with @JsonCreator
works. So, these two cases:
public class AsPojo {
private final int x, y;
@JsonCreator
public AsPojo(@JsonProperty("x") int x, @JsonProperty("y") int y)
{
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
}public record AsRecord(int x, int y) { }
should work almost identically: you can read and write them the same way:
final String DOC = "{ \"x\":1, \"y\":2}";
AsPojo value1 = mapper.readerFor(AsPojo.class).readValue(DOC);
String output1 = mapper.writeValueAsString(value1);AsRecord value2 = mapper.readerFor(AsRecord.class).readValue(DOC);
String output2 = mapper.writeValueAsString(value2);
and except for differences at Java API level (records do not use getters but “plain” accessors — x()
vs getX()
) Record
s should work like immutable value classes.
And this should now be the case: you just use them like POJOs. That’s all there is. As long as you can use Record
s — that is, you are running on JDK 14 or later, and have enabled “preview features” (IDEs require this at least) — Jackson 2.12 and later should recognize these types and handle them appropriately; read and write values like you would expect it to. No special handling by user required!
Annotations supported
If you can use an annotation on @JsonCreator
- annotated constructor of a POJO, you should be able to use it on Record declaration too: so, to rename external name in JSON, you can use @JsonProperty
:
public record InventoryItem(@JsonProperty("Size") int size) { }
(which would mean that in JSON, property name is Size
even if in your code you would access Record field as recordValue.size()
).
Similarly you can you use @JsonDeserialize
to define a custom deserializer to use for a field and so on — anything that can be associated with a property should work with Record constructor arguments.
Small Print
Given that this is a new feature, it is likely that there are edge cases — for example, handling of some annotations (see above) may be incomplete. Bug reports are welcome.
In addition to bug reports, it would be nice to see user reports on successful usage as well — looking forward to more blog posts from early adopters! :)