Waiting for Jackson 2.19.0
(what is in Jackson 2.19.0-rc2?)
Now that we have a release candidate for Jackson 2.19 — 2.19.0-rc2 (*) — it’s time to have a look at what goodies will be included. As usual, the full 2.19 release notes contain detailed list of all the changes; here we’ll look at biggest items.
(*) there is no rc1 — we had a problem with release process that resulted in needing to skip that version.
2.19 Stats
2.19 development has taken almost 6 months since 2.18.0 release, and there are over 80 changes (new features, fixes) across all official Jackson components — a bit more than 2.18 (with about 60 changes). Still, this can once again be classified as a “minor minor release” — partly due to now concurrent development work for finalizing 3.0.0 release (see this blog post for more) — with few bigger features but plenty of bug fixes.
As a result it is expected fewer regressions than with 2.18.0 where there was a major property introspection rewrite.
New modules: jackson-datatype-javax-money, -moneta
There are two related new modules for handling JSR-354 (Java Money) data types:
jackson-datatype-javax-money
: for core Java Money data typesjackson-datatype-moneta
: for supporting extended types in JSR-354 reference implementation
Modules were added in jackson-datatypes-misc repo and are added using standard module registration mechanism
ObjectMapper mapper = JsonMapper.builder()
.addModule(new JavaxMoneyModule())
.addModule(new MonetaMoneyModule())
.build();
String json = mapper.writeValueAsString(Money.of(29.95, "EUR"));
Most-Wanted feature: Support for @JsonWrapped with @JsonCreator
One particularly highly voted-for issue — [databind#1467] — was implemented for 2.19.
This basically (finally!) allows using of @JsonWrapped
with @JsonCreator
like so:
public record Point(int x, int y) { }
public class Rectangle {
private final Point origin;
private int width, height;
@JsonCreator
public Rectangle(@JsonProperty("origin") @JsonUnwrapped Point origin,
int width, int height) {
this.origin = origin;
this.width = width;
this.height = height;
}
public Point getOrigin() { return origin; }
public int getWidth() { return width; }
public int getHeight() { return height; }
}
This is particularly important for Java Record types which cannot have fields or setters; now @JsonUnwrapped
can be used for Record types.
(same is true for immutable types in Kotlin and Scala)
JsonNode functionality improvements
JsonNode
(Tree Model) got a few additional methods added, to support Java 8 Stream
and Optional
types:
- [databind#2145]: Add
optional(String)
(Object nodes) andoptional(int)
(Array nodes) to complement existingget()
andpath()
methods: both returnOptional<JsonNode>
with expected semantics (non-emptyOptional
if there is matching Property value/Array element;Optional.empty()
if not) - [databind#4863]: Add basic
Stream
support:JsonNode.valueStream()
for iterating over Object property values and Array elements,JsonNode.propertyStream()
for iterating over Object properties (name/value pairs) andJsonNode.forEachEntry(BiConsumer<String, JsonNode>)
for alternative way to iterate over Object properties. - [databind#4867]: Add
JsonNode.asOptional()
for essentially wrapping givenJsonNode
asOptional<JsonNode>
— except forMissingNode
which will becomeOptional.empty()
. - [databind#4955]: Add 2 remove methods in
ContainerNode
(that is, forArrayNode
andObjectNode
which are 2 implementations):removeIf(Predicate<? super JsonNode>)
for general purpose removal of Array elements and Object property values, andremoveNulls()
as a convenience short-cut for removingNullNode
s from Arrays or Objects.
Global default EnumNamingStrategy
Assigning naming strategy for “non-standard” Enums (ones where in-Java ALL_UPPER names need to map different in-JSON (external) convention) has been possible for a while using @EnumNaming
annotation like so:
@EnumNaming(EnumNamingStrategies.LowerCamelCaseStrategy.class)
static enum EnumFlavorB {
PEANUT_BUTTER, SALTED_CARAMEL;
}
// in JSON now serialized as "peanutButter" and "saltedCaramel"
but until now there has not been any way to globally specify default naming convention (so default is “use Enum.name()
as-is”) in case all (or most) your Enum types use different convention.
With [databind#4674] it is now possible to specify different default Enum naming strategy
ObjectMapper mapper = JsonMapper.builder()
.enumNamingStrategy(EnumNamingStrategies.LowerCamelCaseStrategy.INSTANCE)
.build();
which in turn may be overridden by @EnumNaming
annotation on class.
Most Improved Modules: Avro, Java 8 date/time, Kotlin
In addition to above set of a dozen changes (all in jackson-databind), what else is changing? Majority of Jackson components had at least one change (see 2.19 Release Notes for details) but most noteworthy appear to be:
jackson-core
(streaming): 7 fixes (2 forJsonPointer
handling)- Data format modules in general, but Avro data format module (
jackson-dataformat-avro
) in particular: 5 fixes/improvements to Schema generation, Logical type handling
(3 other format modules — CSV, XML, YAML — each had 2 fixes) - Java 8 date/time module (
jackson-datatype-jsr310
): 4 fixes (2 for deserialization, 1 for round-tripping (ser + deser) and 1 for performance optimization) - Kotlin module (
jackson-module-kotlin
): 15 changes!
What’s Next?
The plan is to release 2.19.0 soon, start 2.20 branch ((re)named as 2.x as per JSTEP-12), but focus even more on getting 3.0.0 ready for release.
Which reminds me that I need to write a bit about 3.0.0-rc3 that was also released recently.