Skip to content

Jackson 3: comparison/equality operators throw on mismatched or out-of-range operands (e.g. 3.14 = "abc") #432

Description

@wnm3

Summary

After the Jackson 3 migration (#430, PR #431), the comparison (<, >, <=, >=) and equality (=, in) operators can throw a raw, uncaught tools.jackson.databind.node.JsonNodeException instead of returning a value. In Jackson 2 the underlying asDouble()/asLong() calls coerced non‑numeric strings to 0.0 and silently narrowed out‑of‑range integers; Jackson 3 made these coercions fail‑fast (they throw). The affected call sites are unguarded and their lines were not changed by the migration, so the behavior flipped from "return a boolean" to "crash".

This was not caught by the test suite — no existing test exercises these mismatched/out‑of‑range operand combinations.

Where

src/main/java/com/api/jsonata4java/expressions/ExpressionsVisitor.java

  • areJsonNodesEqual (drives = and in): lines 170–177 — left.asDouble() == right.asDouble() and left.asLong() == right.asLong().
  • < operator: lines 1327/1329 (asDouble), 1331/1333 (asLong).
  • > operator: lines 1354/1356, 1358/1360.
  • <= operator: lines 1381/1383, 1385/1387.
  • >= operator: lines 1408/1410, 1411/1414.

In every case the branch is entered as soon as one side is floating‑point (or both are integral), with no check that the other side is actually numeric / in range.

Reproductions

Expression Jackson 2 result Jackson 3 result
3.14 = "abc" false (valid JSONata) throws JsonNodeException
3.14 in ["abc"] false throws
3.14 < "abc" false throws
99999999999999999999 < 5 (integer literal > Long.MAX_VALUE) compared narrowed value throws

The 3.14 = "abc" case is the most serious: comparing a number to a non‑numeric string for equality is legal JSONata and must yield false, not raise an exception.

Verified empirically against jackson‑databind 3.2.0

StringNode("abc").asDouble()      -> THROWS JsonNodeException
StringNode("abc").asLong()        -> THROWS JsonNodeException
BigIntegerNode(99999999999999999999).asLong() -> THROWS JsonNodeException

Suggested fix

Guard each numeric branch so the throwing coercion is only reached for actually‑coercible operands (e.g. require both sides numeric before asDouble()/asLong(), or range‑check with canConvertToLong() and fall back to doubleValue()/bigIntegerValue()), mirroring the guards already added to MatchFunction/ReplaceFunction/ArrayUtils.compare in this migration. Where operand types are genuinely incompatible, throw the controlled EvaluateRuntimeException the code already uses for the string/type‑mismatch branches rather than leaking a Jackson exception.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions