Jackson 3.1.0 released
Jackson 3.1.0 was released 3 weeks ago (on February 23, 2026), bit over 4 months after 3.0.0 release (see this blog post about 3.0.0). One…
Jackson 3.1.0 released
Jackson 3.1.0 was released 3 weeks ago (on February 23, 2026), bit over 4 months after 3.0.0 release (see this blog post about 3.0.0). One release candidate (3.1.0-rc1) was released before the GA version.
The “Big Fix” minor version
The most notable thing about this version is probably the sheer number of changes — mostly fixes, as usual — 143 in total (80 of them in jackson-databind). The number of changes is among the highest for any release, likely second only to the preceding 3.0.0 (164 listed changes).
Many of the issues resolved were quite old: three had been filed more than 10 years ago — oldest, [databind#221], dating back to May 2013! — and 30 of them 5+ years ago. So 3.1.0 can fairly be described as a bit of a “spring-cleaning” release.
The first 3.x Long-Term Support (LTS) version
One noteworthy thing is that 3.1 is the first LTS (see [JSTEP-13]) version in 3.x series. This means that it will be kept open longer than following non-LTS versions. With 3.1.0 release, 3.0 branch is now closed (no more patch releases planned). With 3.1.0 release, Jackson now has 3 LTS branches: 2.18, 2.21 and 3.1.
Show Me The Changes!
For the full listing of all changes, see Jackson 3.1 Release Notes. But let’s have a look at the highlights of 3.1.0 — aside from bug fixes there are plenty of improvements and even some new features.
SBOM publishing fixed
Although 3.0 build did produce CycloneDX SBOM artifacts (as per JSTEP-14), Maven publishing did not upload these as part of the version publishing, rendering this feature mostly useless (one could locally publish these but not access via Maven Central).
But 3.1.0 release published the artifacts: for jackson-databind we have (see https://repo1.maven.org/maven2/tools/jackson/core/jackson-databind/3.1.0/):
jackson-databind-3.1.0-sbom-cyclonedx.json 2026-02-23 21:13 8641
jackson-databind-3.1.0-sbom-cyclonedx.xml 2026-02-23 21:13 7755
Fix here was the same as in 2.21.0. It will be interesting to see if SBOM support proves useful or not — I have not received much (any) feedback since 2.21 release.
Most Wanted issues: 3 resolved
“Most Wanted” for Jackson means issues (mostly in jackson-databind) with at least 5 upvotes: of these 3 were resolved by 3.1.0:
- [databind#1196]: Add support for collecting multiple deserialization failures during processing, not just first one
- [databind#1516]: Problem with multi-argument Creator with
@JsonBackReferenceproperty - [databind#1981]: Add method
remove(JsonPointer)inContainerNode(ArrayNode/ObjectNode)
This leaves about a dozen most-wanted issues still open. Jackson 3.2.0 being developed will fix at least one more.
Most Wanted [databind#1196]: Add support for collecting multiple deserialization failures
The oldest most-wanted issue is probably the most interesting addition.
It builds on existing DeserializationProblemHandler feature: DeserializationProblemHandler allows registering a handler to try to resolve about 10 types of recoverable deserialization problems, from mismatching property names to missing type ids.
In 3.1.0 anew problem handler implementation, CollectingProblemHandler, was added. It will not only provide default handling for problems (mostly just ignoring the issue, providing filler value and so on), but will also collect info on reported problems themselves. The idea is that instead of the read process immediately failing with an exception on the first issue encountered, all problems (up to configurable maximum, by default first 100) are collected to be reported as a set of issues using specific new exception type, DeferredBindingException.
Feature is used like so:
ObjectReader reader = MAPPER
.readerFor(Person.class)
.problemCollectingReader();
Person p;
try {
// note: separate read() call as well
p = reader.readValueCollectingProblems("{ \"name\": \"Bob\" }");
// if we get here, read succeeded normally
} catch (DeferredBindingException e) {
List<CollectedProblem> probs = e.getProblems();
int ix = 0;
for (CollectedProblem prob : probs) {
System.err.printf("Issue #%d at %s: %s.\n",
++ix, prob.getPath(), prob.getMessage());
}
} catch (JacksonException e) {
// Caught non-recoverable problem (invalid JSON etc)
}
Most Wanted [databind#1516]: Problem with multi-argument Creator with @JsonBackReference property
This issue was about a bug in handling of cases like this:
// [databind#1516]
class ParentWithCreator
{
String id, name;
@JsonManagedReference
ChildObject1 child;
@JsonCreator // optional annotation in 3.x
public ParentWithCreator(String id, String name, ChildObject1 child) {
this.id = id;
this.name = name;
this.child = child;
}
}
class ChildObject1
{
public String id, name;
@JsonBackReference
public ParentWithCreator parent;
@JsonCreator // optional annotation in 3.x
public ChildObject1(String id, String name, ParentWithCreator parent) {
this.id = id;
this.name = name;
this.parent = parent;
}
}
which formerly failed but now works in 3.1.0.
Most Wanted [databind#1981]: Add method remove(JsonPointer) in ContainerNode
And the last “most-wanted” issue was about a simple method addition. Now following works:
// NOTE: JsonNode.asObject() added in 3.1 as well (see below)
ObjectNode doc = objectMapper.readTree(json).asObject();
JsonNode maybeRemoved = doc.remove(JsonPointer.compile("/name/first");
// maybeRemoved either node removed (if there was one), or `MissingNode` (if not)
New feature: support alternate radixes (base X) when reading/writing Numbers as (JSON) Strings
The second oldest issue resolved (filed almost 13 (!) years ago) — [jackson-databind#221] — allows reading and writing of “Stringified” numbers using different base (radix) than 10 — for example hexadecimal (base-16).
“Stringied numbers” mean Java numbers written as/read from JSON Strings, not Numbers; alternate radixes are supported for integral number types (byte, short, int,long , BigInteger).
Usage is either by annotations like so:
class HexAddress {
// Read/write as Stringified Hex
@JsonFormat(shape=JsonFormat.Shape.STRING, radix=16)
public int long memAddress;
}
HexAddress hex= new HexNumbers();
hex.memAddress = 64;
JsonMapper mapper = new JsonMapper();
String json = mapper.writeValueAsString(hex);
assertEquals("{\"memAddress\":\"40\"}", json);
Hex result = mapper.readValue(json, HexAddress.class);
assertEquals(64, result.memAddress);
or by configuring certain number types to default to alternate handling using standard Jackson “Config Overrides” mechanism:
ObjectMapper mapper = jsonMapperBuilder()
.withConfigOverride(long.class,
o->o.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING).withRadix(HEX_RADIX)))
.build();
New feature: non-buffering large reads of String values
Another old issue (in fact THE oldest one resolved by 3.1.0) from 2012 (originally [jackson-core#15] — but re-filed as [jackson-core#1228]) — requested addition of streaming alternative for JsonParser.getText() :
public class JsonParser {
// New in 3.1!
public long readString(Writer writer) throws JacksonException;
}
which could be used to access very long JSON String values incrementally, without buffering the whole String in memory (or at least without allocating full String).
This has been implemented for streaming JSON parser backends, as well as DataInput backed one; it is not implemented for non-blocking (async) parser (since it cannot really be implemented wrt Writer being blocking).
Default implementation used by non-JSON format backends does still full buffering — feel free to create issue requesting implementation by specific format backend if interested (implementation has to be done on format-by-format basis unfortunately, but addition is easier with JSON implementation as guideline; esp. by GenAI coding agents).
New annotations: @JsonDeserializeAs, @JsonSerializeAs
One feature added in 2.21.0 that is also included in 3.1.0 is the addition of 2 new “general” annotations (added in jackson-annotations 2.21): they were added to replace use of databind-specific older equivalents (@JsonDeserialize(as=…), @JsonSerialize(as=…)):
interface Value { ... }
class ValueImpl implements Value { ... }
class MyBean2 {
@JsonDeserializeAs(ValueImpl.class)
public Value value;
}
class MyOtherBean2 {
@JsonSerializeAs(Value.class)
public ValueImpl value;
}
See “Jackson 2.21 Released” for the full explanation.
Improvements to JsonNode functionality
In addition to [databind#1981] mentioned earlier (“Most Wanted” feature #3), a few other improvements are also included: improvements that help with streaming/functional style of working with JsonNode values:
- [databind#2343]: “Add
JsonNode.asArray()andJsonNode.asObject()methods”. This adds simple streaming cast methods:ObjectNode doc = objectMapper.readTree(json).asObject();which either cast or throwJsonNodeException(if it not of asserted type) - [databind#3884]: “Add
ObjectNode.put(JsonPointer, JsonNode)method”:doc.put(JsonPointer.compile(“/a/b/c”), doc.textNode(“value”)); - [databind#5558]: “Change defaulting of
JsonNode.asXxx(defaultValue)/JsonNode.asXxxOpt()forNullNode“ As per title, this means that explicitnullvalues in content will be considered same as “missing” values, and default/empty values will be returned instead of Javanull(unlike in 3.0) - [databind#5579]: “Add
JsonNode.map()method“String result = node.map(n -> n.asString().toUpperCase()); - [databind#5581]: “Add functional conversion methods
JsonNode.nullAs(),JsonNode.missingAs()“ Adds 2 convenience methods for replacingNullNodes (from JSONnulls ) andMissingNodes (references to non-existing parts of JSON trees) in streams with specified or suppliedJsonNode:String result = node.nullAs(nullReplValue).missingAs(()->defaultValue()).asString(); - [databind#5586]: “Change
IndexOutOfBoundsExceptionthatArrayNode.set()/replace()throw toJsonNodeException”. Just what it says: avoid JDK IOOBE, throw Jackson’s own exception(s) as necessary.
Improvements to JDK Record type handling
Handling of JDK Record types was improved as well; here are the noteworthy fixes:
- [databind#3079]: “Support
ObjectMapper.updateValue()for Record classes”. [NOTE: also supportsObjectMapper.readerForUpdating()]. This was an interesting improvement to implement and relies on strict structure ofRecordtypes. Now simple usage works as expected:
MyRecord orig = new MyRecord(123, "Bob");
MyRecord updated = objectMapper.updateValue(orig, Map.of("name", "Bill"));
assertEquals(new MyRecord(123, "Bill"), updated);
// or
MyRecord updated2 = objectMapper.readerForUpdating(orig)
.readValue("{\"id\": 456}");
assertEquals(new MyRecord(456, "Bob"), updated2);
Note: Records being immutable, a new instance will (need to be) created and returned.
- [databind#4157]: “Add
MapperFeature.INFER_RECORD_GETTERS_FROM_COMPONENTS_ONLYto ignore getter method auto-detection for Records” This feature, when enabled, will turn off auto-detection of “classic” (get-prefixed) getters and only auto-detect (a) Record components and (b) explicitly annotated getters (@JsonProperty), like so:
record PersonRecord(String name, int age) {
// Helper method that is NOT a record component
public String getDisplayName() {
return name.toUpperCase();
}
@JsonProperty
public int extra() { return 42; }
}
String json = objectMapper.writeValueAsString(
nwe PersonRecord("Bob", 37));
// json ->
//
// {"name":"bob","age":37,"extra":42}
so we will not be getting “displayName” property.
- [databind#5223]: “Java Records missing type information with
DefaultTyping.NON_FINAL(addDefaultTyping.NON_FINAL_AND_RECORDS)” Handling of Record types can be problematic with Default Typing as Record types arefinal— so now there is anewDefaultTyping—NON_FINAL_AND_RECORDS, similar to existingNON_FINAL_AND_ENUMS.
Other improved modules
And for the rest of modules we had a few important fixes as well.
Improved modules: data formats
Aside from core modules (jackson-core, 11; jackson-databind, 80 issues resolved), most improvements were to data format modules, with total of 33 issues resolved. Here are most notable ones:
Improved dataformat: Avro
- jackson-dataformat-avro: [avro#514] “Update to Apache Avro 1.12.1 — the latest version of Apache Avro is now used (when using Apache backend for writing (always) or reading (optional)”
Improved dataformat: CSV
- csv#479: “
STRICT_CHECK_FOR_QUOTINGdoes not quote value that contains newline character” - csv#579: “Incorrect detection of missed columns in header line if columns reordering is enabled”
- csv#601: “Reader should allow separating plain
nullValueand quoted value"nullValue"“ - csv#608: “Fix issue with UTF-8 surrogate pair decoding”
- csv#613: “Support
StreamReadConstraints.maxDocumentLength()validation for CSV module”
Improved dataformat: YAML
- yaml#568: “YAML —
ScannerExceptionon block scalar “\n”” - yaml#590: “Upgrade to the latest version of SnakeYAML Engine (3.0.1)”
- yaml#596: “Port
YAMLAnchorReplayingFactoryfrom 2.x and improve it to handle nested anchors” - yaml#608: “Fix issue with UTF-8 surrogate pair decoding”
- yaml#609: “Catch and rethrow yaml engine internal exception on generation”
Improved modules: Guava datatype
- guava#211: “
GuavaMultimapDeserializerdoes not respectJsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY“
Improved modules: Blackbird
- blackbird#334: ”
BlackbirdModuledoes not implementjava.io.Serializable“
Improved modules: Mr Bean
- mr-bean#320: “Remove
byte-buddyshading from 3.1 MrBean” — now ByteBuddy is included as a regular dependency, solving possible issues with JPMS.
Next Up: Jackson 3.2 with Yet More Fixes!
So the next thing to focus on is 3.2 — and its focus will remain on clearing up the backlog: while 3.2 brough down jackson-databind open issue count from over 300 closed to 200, there’s still way to go.
So far 3.2 has about 30 fixes (see Jackson Release 3.2 page) but as with 3.1, the focus in on clearing the backlog focusing with oldest open issues — these tend to be hardest ones to solve. Still, progress now is faster than for preceding 10 years or so!
Stay tuned. :)
메타데이터
- post_id
- f3c962e4329d
- slug
- jackson-3-1-0-released-f3c962e4329d
- url
- https://medium.com/@cowtowncoder/jackson-3-1-0-released-f3c962e4329d
- canonical_url
- https://medium.com/@cowtowncoder/jackson-3-1-0-released-f3c962e4329d
- author_url
- https://medium.com/@cowtowncoder
- status
- ok
- fetched_at
- 2026-06-25 12:15:08