Skip to content

Upgrade jackson from 2.22.0 to 3.2.0#628

Draft
sabieber wants to merge 4 commits into
mainfrom
feature/sbi/SIRI-1149
Draft

Upgrade jackson from 2.22.0 to 3.2.0#628
sabieber wants to merge 4 commits into
mainfrom
feature/sbi/SIRI-1149

Conversation

@sabieber

@sabieber sabieber commented Jul 10, 2026

Copy link
Copy Markdown
Member

BREAKING CHANGES

Upgrades Jackson from 2.22.0 to 3.2.0 across the sirius stack. Jackson 3 uses new Maven coordinates and Java packages — see the official Jackson 3 migration guide and release notes for the complete list of renames and behavioral changes.

What applications using the sirius framework have to change:

  • Rename imports com.fasterxml.jackson.*tools.jackson.* (annotations stay on com.fasterxml.jackson.annotation); drop jackson-datatype-jsr310 and JavaTimeModule registrations (built-in now).
  • Fix the resulting compile errors: unchecked exceptions (JsonProcessingExceptionJacksonException, dead catch (IOException) blocks), method renames (asText()asString() etc., writeFieldName()writeName() etc.), builder-based mapper/generator configuration — see the migration guide for details.

Description

Migrates sirius-kernel from Jackson 2.22.0 to Jackson 3.2.0 (managed via the Jackson 3 BOM in sirius-parent, see related PR).

Dependency changes

  • jackson-databind moves to the new tools.jackson.core coordinates (Jackson 3 uses new Maven coordinates and Java packages, so both major versions can coexist on the classpath for third-party dependencies still using Jackson 2).
  • jackson-annotations intentionally stays on the com.fasterxml.jackson 2.x line — Jackson 3 continues to use it, so all @Json... annotations keep working unchanged.
  • jackson-datatype-jsr310 is removed, as java.time support is built into jackson-databind 3.

Json

  • ObjectMappers are immutable in Jackson 3, so Json.MAPPER is now constructed via the builder API. It is based on JsonMapper.builderWithJackson2Defaults(), which keeps the wire format and parsing behavior stable for API consumers, since Jackson 3 flips several defaults (alphabetical property ordering, date/enum handling, stricter parsing). Adopting the new defaults is left as a deliberate, separate step.
  • Jackson exceptions are unchecked now; the try... methods keep their signatures with JacksonException replacing JsonProcessingException.

Amount

  • Jackson 3 no longer honors @JsonValue on subclasses of Number — the standard number serializer takes precedence and would emit the locale-dependent toString() representation (see upstream regression, to be reported against jackson-databind). Since class-level @JsonSerialize annotations are consulted before the standard handling, Amount now declares an explicit nested serializer replicating the previous behavior. Unlike registering a serializer on Json.MAPPER, this also covers mappers constructed elsewhere and node serialization via toString().
  • The new behavior is pinned by additional tests (NOTHINGnull, rounding of non-rounded amounts, full precision for rounded amounts).

Notes for consumers

  • The JSTEP-3 accessor renames apply throughout: asText()asString(), textValue()stringValue(), isTextual()isString().
  • The renamed scalar accessors also changed behavior: Jackson 2 leniently returned null/0/false/"" for missing, null-valued or type-mismatched properties — Jackson 3 mostly throws JsonNodeException instead. Both behaviors are pinned by executable documentation tests in JsonTest (Jackson 3 version in this branch; Jackson 2 counterpart on feature/sbi/SIRI-1149-before). The test-verified differences:
Accessor (2.x → 3.x name) Case Jackson 2 Jackson 3 Behavior-preserving replacement
textValue()stringValue() wrong type (e.g. number) null throws stringValue(null)
missing property null throws stringValue(null)
JSON null null null (unchanged)
asText()asString() JSON null the string "null" "" asString("") ¹
container node (object/array) "" throws asString("")
longValue() numeric string "42" 0 throws longValue(0L) ²
any wrong type 0 throws longValue(0L)
JSON null / missing 0 throws longValue(0L)
asLong() non-numeric string "foo" 0 throws asLong(0L)
booleanValue() any non-boolean false throws booleanValue(false)
JSON null / missing false throws booleanValue(false)
asBoolean() non-convertible string "foo" false throws asBoolean(false)

¹ deliberately yields "" instead of Jackson 2's literal "null" — code actually depending on the string "null" needs a manual isNull() check.
² asLong(0L) is not equivalent here: it would convert "42" to 42 where Jackson 2's longValue() returned 0.

Unchanged: matching-type reads, the asXxx() scalar coercions (asString() on numbers, asLong() on "42", asBoolean() on "true"/numbers) and the asXxx() defaults for null/missing. Where absence should be handled explicitly rather than defaulted, prefer the Optional variants (stringValueOpt(), longValueOpt(), ...).

Additional Notes

Checklist

  • Code change has been tested and works locally
  • Code was formatted via IntelliJ and follows SonarLint & best practices
  • Patch Tasks: Is local execution of Patch Tasks necessary? If so, please also mark the PR with the tag.

…pgrade

Assisted-by: Claude Fable 5
Fixes: SIRI-1149
@sabieber sabieber added 💣 BREAKING CHANGE Contains non-backwards compatible changes to public methods or changes the behavior of existing code ⬆️ Dependencies Pull requests that update a dependency file 🛠️ Maintenance Translations, Code Cleanup, ... 🤖 AI-Assisted Created with significant assistance of artificial intelligence tools labels Jul 10, 2026
Switches jackson-databind to the new tools.jackson.core coordinates and adjusts all imports accordingly. The jackson-annotations artifact intentionally stays on the com.fasterxml.jackson 2.x line, as Jackson 3 continues to use it. The jackson-datatype-jsr310 dependency is removed because java.time support is built into jackson-databind 3.

Json.MAPPER is now constructed via the builder API (mappers are immutable in Jackson 3) based on builderWithJackson2Defaults(), so the wire format and parsing behavior remain unchanged for API consumers despite Jackson 3 flipping several serialization defaults (alphabetical property ordering, dates, enums, stricter parsing). Adopting the new defaults remains a deliberate, separate step.

Jackson 3 no longer honors @jsonvalue on subclasses of Number, which silently broke Amount serialization to a locale-dependent string. As class-level @JsonSerialize annotations are consulted before the standard Number handling, Amount now declares an explicit serializer replicating the previous behavior. Unlike a mapper-registered module, this also covers mappers constructed outside of Json and node serialization via toString().

Note that node.toString() no longer serializes POJO nodes through the mapper - Json.write() must be used instead. Jackson exceptions are now unchecked; the try-prefixed methods in Json keep their signatures with JacksonException in place of JsonProcessingException.

Assisted-by: Claude Fable 5
Fixes: SIRI-1149
@sabieber sabieber changed the title Expand JSON tests to pin current amount serialization behaviour pre u… Upgrade jackson from 2.22.0 to 3.2.0 Jul 10, 2026
@sabieber
sabieber marked this pull request as draft July 10, 2026 12:31
sabieber added 2 commits July 14, 2026 11:08
Jackson 2 serialized Amount via its @jsonvalue accessor, whose JsonValueSerializer treated a null accessor result as empty, so that @JsonInclude(NON_EMPTY) suppressed properties holding empty amounts. The explicit serializer introduced in the Jackson 3 migration only replicated the value output, causing such properties to be written as null instead of being omitted.

Overrides isEmpty() in Amount.JacksonSerializer to treat NOTHING as empty, restoring the Jackson 2 behavior. This surfaced in sellsite, where a model class annotated with @JsonInclude(NON_EMPTY) suddenly emitted "maxValue": null entries that Jackson 2 omitted.

Assisted-by: Claude Fable 5
Fixes: SIRI-1149
This seems a bit quirky so it seems like a good idea to document his and also as a guard against undocumented behaviour changes (which the maintainers are known for).

Assisted-by: Claude Fable 5
Fixes: SIRI-1149
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖 AI-Assisted Created with significant assistance of artificial intelligence tools 💣 BREAKING CHANGE Contains non-backwards compatible changes to public methods or changes the behavior of existing code ⬆️ Dependencies Pull requests that update a dependency file 🛠️ Maintenance Translations, Code Cleanup, ...

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants