Jackson 3.0.0-rc2 (minor update)
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 JacksonException
s 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()
: returnsint
if (and only if) node contains Number that can be expressed as 32-bit Javaint
; if not, will throwJsonNodeException
.
Valid JSON values would include32
(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-bitint
range (1.0E100) and so on.int intValue(int defaultValue)
: Same asintValue()
for validint
values, but for non-valid will returndefaultValue
instead of throwingJsonNodeException
OptionalInt intValueOpt()
: Same asintValue()
(except for return type) but instead of throwingJsonNodeException
returnsOptionalInt.empty()
for non-valid node valuesint asInt()
: Similar tointValue()
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 asint
value of137
). For other value types, other coercions allowed (forboolean
, integral values accepted).
Will thrown aJsonNodeException
if none of coercions applicableint asInt(int defaultValue)
: Same asasInt()
but if coercions fail, will returndefaultValue
instead of throwingJsonNodeException
.OptionalInt asIntOpt():
Similar toasInt()
(except for return type) but if coercions fail, will returnOptionalInt.empty()
instead of throwingJsonNodeException
.
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 Javafloat
(no coercions, regular type and range checks)numberValue()
for accessing any of numeric types as its naturalNumber
representation (no coercions, no range checks needed)shortValue()
for 16-bit Javashort
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:
- Finally fix Gradle dependency issue (… but we do need your help to verify!)
- Probably merge “Java 8 date/time” module (
jackson-datatype-jsr310
) injackson-databind
(one less module to have to register) — https://github.com/FasterXML/jackson-databind/pull/5032 - Replace core Reflection use with
MethodHandles
— https://github.com/FasterXML/jackson-databind/pull/5046
When is that? Hopefully within next 2 weeks or so.
Happy Coding & Download with Care. :)