Sitemap

Jackson 3.0.0-rc2 (minor update)

4 min readApr 1, 2025

It has not been very long since Jackson 3.0.0-rc1 was released but in order to gather feedback quickly — to find issues with major version upgrade, specifically — we decided to release RC2 few days ago.

One driver for RC2 was a reported Gradle dependency problem in RC1 — but unfortunately turns out this was only partially resolved (see here for follow-up). So (some?) Gradle users may need to wait until RC3 (I am not 100% sure if there is a work-around).

But aside from this unfortunate problem, there are 3 updates in RC2 (over RC1) to consider.

1. Remove need to use @JsonSubTypes for Sealed Classes

One useful improvement in JDK 17 (the baseline for Jackson 3) is thing called “Sealed Classes” (see Baeldung’s article for explanation if not familiar). With regular classes, polymorphic handling with type names requires use of not only @JsonTypeInfo in the base class, but also adding references to sub-classes using either ObjectMapper.registerSubtypes() or (more commonly) @JsonSubTypes annotation like so:

    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="type")
@JsonSubTypes({ @JsonSubTypes.Type(ImplX.class),
@JsonSubTypes.Type(ImplY.class),
@JsonSubTypes.Type(ImplZ.class)
})
abstract class BaseX { }

but with sealed classes and Jackson 3.0.0-rc2, you only need @JsonTypeInfo as marker (and definition of which type id to use):

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="type")
abstract sealed class BaseX permits ImplX, ImplY, ImplZ { }

which works pretty well as sub-type definitions are required for sealed classes anyway — and you no longer can accidentally forget them.

2. Extended Accessors for JsonNode

One of bigger efforts with 3.0 has been JSTEP-3 — improvements to Jackson Tree Model, that is, to JsonNode and its subtypes. There have been quite a few actual smaller improvements (see Javadocs for 3.0.0-rc2 as well as JSTEP-3), but there was one last bigger task left: unification and extension of “xxxValue() / asXxx()” accessors. Unification means making accessors work uniformly along same rules (for numbers, checking that value fits range; that specific coercions are similarly allowed/disallowed); and extensions means adding all variants similarly. Some of the work is just making use of other additions — for example, now that JacksonExceptions are unchecked, a new exception type JsonNodeException may be thrown by accessors in problem cases; something not possible to change in 2.x (for backwards-compatibility).

The last part of this unification/extension was implemented via [databind#5003] and [databind#5034], adding a set of accessors for following types:

  • int / long / BigInteger
  • double / BigDecimal
  • String
  • boolean

There are 6 now different accessors for each of types listed, following pattern shown here for int accessors:

  • int intValue(): returns int if (and only if) node contains Number that can be expressed as 32-bit Java int ; if not, will throw JsonNodeException.
    Valid JSON values would include 32 (regular integer that fits in range), 5.0 (number with empty fraction), 7.01E3 (scientific-notation but still integer). Invalid values would be Strings (“abc”, “10”), Booleans, Numbers with non-empty fractions (2.57), numbers outside 32-bit int range (1.0E100) and so on.
  • int intValue(int defaultValue): Same as intValue() for valid int values, but for non-valid will return defaultValue instead of throwing JsonNodeException
  • OptionalInt intValueOpt(): Same as intValue() (except for return type) but instead of throwing JsonNodeException returns OptionalInt.empty() for non-valid node values
  • int asInt(): Similar to intValue() if we have valid integral number (returned as-is), but will allow additional coercion from some representations.
    Specifically will allow “Stringified” numbers (so String "137" would be returned as int value of 137 ). For other value types, other coercions allowed (for boolean , integral values accepted).
    Will thrown a JsonNodeException if none of coercions applicable
  • int asInt(int defaultValue): Same as asInt() but if coercions fail, will return defaultValue instead of throwing JsonNodeException.
  • OptionalInt asIntOpt(): Similar to asInt() (except for return type) but if coercions fail, will return OptionalInt.empty() instead of throwing JsonNodeException.

Whoa! That’s quite a few methods added. Beyond this extensive set of accessors, there are a few supporting typed accessors (pre-existing but improved):

  • binaryValue() for both native Binary values (for formats that support them — Avro, CBOR, Protobuf, Smile) and Base64-encoded String values (JSON and other textual formats)
  • floatValue() for 32-bit Java float (no coercions, regular type and range checks)
  • numberValue() for accessing any of numeric types as its natural Number representation (no coercions, no range checks needed)
  • shortValue() for 16-bit Java short s (no coercions, regular type and range checks)

Feedback wrt validation rules, ergonomics would be much appreciated.

3. Fix to getter/setter naming rules (no detecting island() :) )

And last & probably the least, [databind#2882] was fixed for 3.0.0-rc2 and following methods no longer infer properties:

public interface Beano {
// Used to produce "_value"; now ignored unless annotated
public int get_value();
// Used to produce "land"; - "" -
public boolean island();
// Would be expected to set property named "ter"
public void setter(int x);
}

which should remove occasional odd cases of “where did THAT come from?” when serializing POJOs.

4. Towards 3.0.0-RC3

And the next step is releasing RC3. The plan is for it to:

When is that? Hopefully within next 2 weeks or so.

Happy Coding & Download with Care. :)

--

--

@cowtowncoder
@cowtowncoder

Written by @cowtowncoder

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

No responses yet