Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonNumEquals fails to compare IntNode and LongNode with same value #45

Open
grimsa opened this issue Jan 22, 2020 · 1 comment
Open

Comments

@grimsa
Copy link

grimsa commented Jan 22, 2020

Problem

JsonNode intNode = IntNode.valueOf(1);
JsonNode longNode = LongNode.valueOf(1);
boolean same = JsonNumEquals.getInstance().equivalent(intNode, longNode);
// at this point same == false

Cause
In com.github.fge.jackson.JsonNumEquals#numEquals:

private static boolean numEquals(final JsonNode a, final JsonNode b)
{
    /*
     * If both numbers are integers, delegate to JsonNode.
     */
    if (a.isIntegralNumber() && b.isIntegralNumber())     // this is true
        return a.equals(b);           // yet this is false

    /*
     * Otherwise, compare decimal values.
     */
    return a.decimalValue().compareTo(b.decimalValue()) == 0;
}
@grimsa
Copy link
Author

grimsa commented Jul 31, 2021

Workaround

Since Jackson 2.6.0 there is JsonNode#equals(Comparator<JsonNode>, JsonNode) method [1].

The numeric equality comparator can be implemented like this:

class NumericEqualityComparator implements Comparator<JsonNode> {
    NumericEqualityComparator() {
    }

    @Override
    public int compare(JsonNode a, JsonNode b) {
        return areNodesNumericallyEqual(a, b)
                ? 0
                : 1;
    }

    private boolean areNodesNumericallyEqual(JsonNode a, JsonNode b) {
        return a.isNumber() && b.isNumber()
                ? areNumberNodesNumericallyEqual(a, b)
                : a.equals(b);
    }

    private boolean areNumberNodesNumericallyEqual(JsonNode a, JsonNode b) {
        if (a.isIntegralNumber() && b.isIntegralNumber()) {
            return a.canConvertToLong() && b.canConvertToLong()
                    ? a.longValue() == b.longValue()                        // Comparing as long to avoid BigInteger allocations
                    : a.bigIntegerValue().equals(b.bigIntegerValue());
        }
        return a.isBigDecimal() || b.isBigDecimal()
                ? a.decimalValue().compareTo(b.decimalValue()) == 0
                : Double.compare(a.doubleValue(), b.doubleValue()) == 0;    // Comparing as double to avoid BigDecimal allocations
    }
}

And then JsonNode comparison is performed like this node.equals(new NumericEqualityComparator(), otherNode)

[1] FasterXML/jackson-databind#790

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant