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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions