Skip to content

Migrate from Jackson 2.x to Jackson 3 (tools.jackson) (#430) - #431

Merged
wnm3 merged 9 commits into
masterfrom
jackson-3-migration
Jul 7, 2026
Merged

Migrate from Jackson 2.x to Jackson 3 (tools.jackson) (#430)#431
wnm3 merged 9 commits into
masterfrom
jackson-3-migration

Conversation

@wnm3

@wnm3 wnm3 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates JSONata4Java from com.fasterxml.jackson 2.x to tools.jackson 3.2.0 and cuts the major version to 3.0.0. Closes #430.

  • Dependencies (pom.xml): switch to tools.jackson.core:jackson-databind and tools.jackson.dataformat:jackson-dataformat-xml 3.2.0; remove the Jackson 2.x jackson-databind and jackson-dataformat-xml blocks (woodstox-core retained); bump project version 2.6.43.0.0.
  • Package/API renames across main + test: com.fasterxml.jackson.*tools.jackson.*, TextNodeStringNode, JsonFactorytools.jackson.core.json.JsonFactory, JsonProcessingExceptionJacksonException, JsonMappingExceptionDatabindException, immutable JsonMapper/XmlMapper builders.
  • Version string bumped in README.md (×3) and the launch scripts tester.sh, testerui.sh, testerui.cmd.

Behavior-preserving fixes for Jackson 3 default/API changes

No test assertion or expected value was changed — every fix restores the Jackson 2 behavior at its source:

  • MatchFunction/ReplaceFunction: guard the pattern emptiness checks so a POJONode-wrapped RegularExpression never reaches the now-throwing asText(); the boolean logic is identical to Jackson 2.
  • ArrayUtils.compare: pass "" as the asText() default so container operands coerce to "" (Jackson 2 behavior) instead of throwing.
  • AgnosticTestSuite: normalize whole-number doubles with (long) d, replicating Jackson 2's silent narrowing (Jackson 3's asLong() throws on out-of-long-range values).
  • Utils test mapper: disable FAIL_ON_TRAILING_TOKENS to restore the Jackson 2 default so existing test inputs parse identically.
  • catch (IOException)catch (JacksonException) where Jackson 3 methods no longer throw checked IOException.

Documentation

  • Added a "Retained Jackson 2 behaviors (as-built)" section to docs/specs/Jackson-3-Migration-Design.md recording each compatibility decision above (with how to reverse it if the project later adopts the stricter Jackson 3 semantics), plus the pre-existing Tester EOF NPE, the intentionally-tolerated "[\"h11\", \"h21\"]]" trailing-] test-data typo, and the acceptable transitive jackson-annotations dependency.
  • Added explanatory comments at the MatchFunction/ReplaceFunction guard sites so the isTextual()-guarded emptiness checks are self-documenting.

Maven build-warning cleanup (unrelated to Jackson)

Fixed three pre-existing pom.xml warnings that appeared on every build:

  • bnd private-reference (×2): added com.api.jsonata4java.expressions.regex to Export-Package — the exported com.api.jsonata4java / com.api.jsonata4java.expressions packages expose regex types (RegexEngine, RegexPattern, …) in their public API, so that sub-package must be exported too.
  • additionalClasspathElements unknown (×2): removed the empty element from maven-compiler-plugin (not a valid parameter).
  • excludes unknown: removed from the maven-assembly-plugin single goal (not a valid parameter; silently ignored — no effect on the produced jar).

A separate gpg.passphrase deprecation warning originates from the developer’s local ~/.m2/settings.xml, not this repo, and is resolved locally via gpg-agent.

Verification

  • mvn clean testBUILD SUCCESS, 1223 tests, 0 failures, 93 skipped.
  • mvn package → builds JSONata4Java-3.0.0.jar; OSGi Import-Package header references only tools.jackson.* [3.2,4) (no com.fasterxml.jackson.*).
  • dependency:tree shows only tools.jackson.*:3.2.0 plus the acceptable transitive jackson-annotations.
  • Tester CLI evaluates end-to-end ($sort([3,1,2])[1,2,3]); XML round-trip smoke-tested via XmlMapper.builder().

Notes

  • Pre-existing (unrelated to this migration) Tester REPL NullPointerException on stdin EOF at Tester.java:156 was left as-is (now recorded in the design spec's known-items section).

🤖 Generated with Claude Code

@wnm3

wnm3 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Resolves Dependabot alert #7 (CVE-2026-54515 / GHSA-5jmj-h7xm-6q6v, jackson-databind, medium / CVSS 5.3).

The default branch's pom.xml is in a half-migrated state and still declares the vulnerable com.fasterxml.jackson.core:jackson-databind:2.22.0 (vulnerable range >= 2.22.0, < 2.22.1; patched in 2.22.1), which is what triggers the alert.

This PR removes the legacy com.fasterxml.jackson.core/dataformat databind entries entirely, leaving only tools.jackson:3.2.0. Since 3.2.0 > 3.1.4 (the 3.x patched version), the resolved dependency is beyond the fix and not in any vulnerable range — dependency:tree shows only tools.jackson.core:jackson-databind:3.2.0 / jackson-core:3.2.0. The one remaining com.fasterxml artifact is the transitive jackson-annotations:2.22, which is not affected by this advisory (it's a jackson-databind bug).

Merging this PR therefore clears alert #7. (Real-world exposure was already negligible: JSONata4Java parses JSON into a tree via readTree/JsonNode rather than doing POJO bean deserialization with @JsonIgnoreProperties + ACCEPT_CASE_INSENSITIVE_PROPERTIES, so the vulnerable code path isn't reachable through its API.)

wnm3 and others added 9 commits July 7, 2026 17:29
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
…cedure.txt (#430)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
Move the codebase from com.fasterxml.jackson 2.x to tools.jackson 3.2.0.

- pom.xml: use tools.jackson.core:jackson-databind and
  tools.jackson.dataformat:jackson-dataformat-xml 3.2.0; remove the
  Jackson 2.x databind and dataformat-xml deps (woodstox-core retained);
  bump project version 2.6.4 -> 3.0.0.
- Package/class/method renames across main and test sources:
  com.fasterxml.jackson.* -> tools.jackson.*, TextNode -> StringNode,
  JsonFactory -> tools.jackson.core.json.JsonFactory,
  JsonProcessingException -> JacksonException,
  JsonMappingException -> DatabindException, immutable mapper builders.

Behavior-preserving fixes for Jackson 3 default/API changes (no test
assertion or expected value was changed):
- MatchFunction/ReplaceFunction: guard the pattern emptiness checks so a
  POJONode-wrapped RegularExpression never reaches the now-throwing
  asText(); logic is identical to Jackson 2.
- ArrayUtils.compare: pass "" default to asText() so container operands
  coerce to "" as they did under Jackson 2 instead of throwing.
- AgnosticTestSuite: normalize whole-number doubles with (long) d,
  replicating Jackson 2's silent narrowing (Jackson 3's asLong() throws
  on out-of-long-range values).
- Utils test mapper: disable FAIL_ON_TRAILING_TOKENS to restore the
  Jackson 2 default so existing test inputs parse identically.
- catch (IOException) -> catch (JacksonException) where Jackson 3 methods
  no longer throw checked IOException.
- README and launch scripts (tester.sh, testerui.sh, testerui.cmd):
  version 2.6.4 -> 3.0.0.

Verified: mvn clean test green (1223 tests, 0 failures, 93 skipped);
mvn package builds JSONata4Java-3.0.0.jar with an OSGi Import-Package
header referencing only tools.jackson.* [3.2,4); Tester CLI evaluation
and XML round-trip smoke-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
- Add a "Retained Jackson 2 behaviors (as-built)" section to the design
  spec listing each compatibility decision (container asText(""),
  POJONode emptiness guards, (long) d narrowing, FAIL_ON_TRAILING_TOKENS,
  unchecked-exception catches), plus the pre-existing Tester EOF NPE and
  the acceptable transitive jackson-annotations dependency.
- Add explanatory comments at the MatchFunction/ReplaceFunction guard
  sites so the isTextual()-guarded emptiness checks are self-documenting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
…shes (#433)

Jackson 3 makes asInt()/asLong()/textValue()/asBoolean() fail-fast: they
throw JsonNodeException for out-of-range or wrong-type nodes where Jackson 2
silently narrowed/coerced. Several unchanged call sites therefore flipped from
"return a value" to "crash with a raw Jackson exception". Fix them to use the
widest-safe representation (BigInteger/double) or a controlled JSONata error,
matching jsonata.org 2.2.1 where known.

#433 numeric coercion:
- ExpressionsVisitor.isWholeNumber(JsonNode): use the double overload instead
  of asInt()==asDouble() (threw for integral doubles > int range, e.g. 1e19).
- Array index: only select when canConvertToInt(); out-of-int-range indices are
  out of bounds and select nothing (e.g. ["a","b"][3000000000] -> undefined).
- Range visitSeq: compute bounds and size with BigInteger, raising the controlled
  "sequence too big" error for [1..1e19]/[1..3000000000] (also fixes a latent int
  count overflow).
- Unary minus: negate an out-of-long-range BigIntegerNode exactly.
- Equality (=) and ordering (< > <= >=): compare integral operands via exact
  bigIntegerValue(), hardening the same crash class on BigInteger operands.
- SumFunction: oversized integral elements fall back to the double-sum path.
- FromMillis/FromMillisZoned: convert an out-of-range magnitude to a controlled
  "Number out of range" error while preserving fractional-millis truncation.
- FormatBase: use the full long value + Long.toString instead of a 32-bit int
  ($formatBase(5890840712243076) -> "5890840712243076", was "1008002948").

Related migration compatibility fixes swept in:
- JoinFunction (#434): every element must be a string; error on any non-string
  (including nested arrays) instead of flattening, matching the reference and
  avoiding textValue() throwing.
- BooleanUtils: return false for a BinaryNode (JSON/reference has no binary type;
  Jackson 3's no-arg asBoolean() throws) consistent with prior behavior.
- Ordering operators: array/object operands raise "must evaluate to numeric or
  string values" rather than returning null.

Adds BasicExpressionsTests.testOutOfRangeNumericCoercion documenting the fixed
behavior. Full suite: 1223 run, 0 failures, 0 errors, 93 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
- Export com.api.jsonata4java.expressions.regex so bnd no longer flags
  it as a private reference leaked through the public API of the
  com.api.jsonata4java and com.api.jsonata4java.expressions exports.
- Remove empty <additionalClasspathElements/> from maven-compiler-plugin
  (not a valid parameter for that plugin).
- Remove <excludes> from maven-assembly-plugin single goal (not a valid
  parameter; exclusions belong in an assembly descriptor).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
Record the pom.xml build-warning fixes (bnd private-reference exports,
invalid maven-compiler-plugin/maven-assembly-plugin parameters) and note
that the gpg.passphrase deprecation originates from local settings.xml,
not the project pom.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: wnm3 <wnm3@us.ibm.com>
@wnm3
wnm3 force-pushed the jackson-3-migration branch from 9831c86 to a563a76 Compare July 7, 2026 21:29
@wnm3
wnm3 merged commit 01f1157 into master Jul 7, 2026
4 checks passed
@wnm3 wnm3 mentioned this pull request Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant