Skip to content

Support handling of non-finite numbers if present#1241

Merged
stevehu merged 2 commits intonetworknt:masterfrom
justin-tay:nonfinite
Apr 5, 2026
Merged

Support handling of non-finite numbers if present#1241
stevehu merged 2 commits intonetworknt:masterfrom
justin-tay:nonfinite

Conversation

@justin-tay
Copy link
Copy Markdown
Collaborator

Closes #1239, Closes #1240

If NaN, Infinity, -Infinity values are present they are not treated as a JSON number type as it is not valid JSON.

When used in const and enum comparison is done by checking the values and not using numeric comparison. I.e. for const NaN the instance data NaN will pass validation.

When used in type such non-finite numbers are not considered number.

When used in minimum, maximum, exclusiveMaximum, exclusiveMinimum and multipleOf as these are not numbers no validation is performed similar to when the instance data is a string.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the validator to handle non-finite numeric values (NaN, Infinity, -Infinity) by treating them as not valid JSON numbers for type and numeric comparison keywords, while still allowing const/enum matching by value when those non-finite values appear in schemas and instances.

Changes:

  • Added non-finite detection (isNonFiniteNumber) and used it to exclude non-finite values from number/integer type classification.
  • Updated const/enum numeric normalization/comparison paths to support non-finite values.
  • Added/updated tests across type and numeric keywords to cover non-finite handling.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/main/java/com/networknt/schema/utils/JsonNodeTypes.java Adds isNonFiniteNumber and excludes non-finite values from isNumber.
src/main/java/com/networknt/schema/utils/TypeFactory.java Returns UNKNOWN for non-finite numeric nodes instead of NUMBER.
src/main/java/com/networknt/schema/keyword/MultipleOfValidator.java Skips multipleOf validation when instance value is non-finite.
src/main/java/com/networknt/schema/keyword/EnumValidator.java Normalizes non-finite numeric enum values using DoubleNode to enable matching.
src/main/java/com/networknt/schema/keyword/ConstValidator.java Adds a non-finite comparison branch for numeric const validation.
src/test/java/com/networknt/schema/TypeValidatorTest.java Adds tests asserting non-finite values fail integer/number type validation.
src/test/java/com/networknt/schema/MultipleOfValidatorTest.java Adds tests asserting multipleOf does not validate non-finite instance numbers.
src/test/java/com/networknt/schema/MinimumValidatorTest.java Adds non-finite tests; modifies overflow-related assertions.
src/test/java/com/networknt/schema/MaximumValidatorTest.java Adds non-finite tests; modifies overflow-related assertions.
src/test/java/com/networknt/schema/ExclusiveMinimumValidatorTest.java Adds tests asserting exclusiveMinimum ignores non-finite instance numbers.
src/test/java/com/networknt/schema/ExclusiveMaximumValidatorTest.java Adds tests asserting exclusiveMaximum ignores non-finite instance numbers.
src/test/java/com/networknt/schema/EnumValidatorTest.java Adds enum tests for NaN/±Infinity matching and mismatch cases.
src/test/java/com/networknt/schema/ConstValidatorTest.java Adds const tests for NaN/±Infinity matching and mismatch cases.
Comments suppressed due to low confidence (4)

src/test/java/com/networknt/schema/MaximumValidatorTest.java:213

  • Filtering out "type" errors here can mask expected type violations for non-finite/overflow-to-Infinity values. Prefer asserting the type error(s) explicitly or adjust the schema under test.
            List<Error> messages2 = v.validate(doc).stream().filter(e -> !e.getKeyword().equals("type")).toList();
            if (Double.valueOf(maximum).equals(Double.POSITIVE_INFINITY)) {
                assertTrue(messages2.isEmpty(), format("Maximum %s and value %s are equal, thus no schema violation should be reported", maximum, value));
            } else {
                assertFalse(messages2.isEmpty(), format("Maximum %s is smaller than value %s ,  should be validation error reported", maximum, value));
            }

src/test/java/com/networknt/schema/MaximumValidatorTest.java:225

  • Filtering out "type" keyword errors and asserting emptiness can conceal expected type failures for non-finite values. Prefer asserting the type error(s) or removing "type" from the schema for this scenario.
            List<Error> messages3 = v.validate(doc).stream().filter(e -> !e.getKeyword().equals("type")).toList();
            //when the schema and value are both using BigDecimal, the value should be parsed in same mechanism.
            String theValue = value.toLowerCase().replace("\"", "");
            if (maximum.toLowerCase().equals(theValue)) {
                assertTrue(messages3.isEmpty(), format("Maximum %s and value %s are equal, thus no schema violation should be reported", maximum, value));
            } else {
                assertFalse(messages3.isEmpty(), format("Maximum %s is smaller than value %s ,  should be validation error reported", maximum, value));
            }

src/test/java/com/networknt/schema/MaximumValidatorTest.java:281

  • Filtering out "type" errors here can hide expected type failures for non-finite values. Prefer asserting the type error(s) explicitly or adjust the schema under test. Also remove the extra semicolon after toList().
        messages = v.validate(doc).stream().filter(e -> !e.getKeyword().equals("type")).toList();;
        // "1.7976931348623158e+308" == "1.7976931348623157e+308" == Double.MAX_VALUE
        // new BigDecimal("1.7976931348623158e+308").compareTo(new BigDecimal("1.7976931348623157e+308")) > 0
        assertTrue(messages.isEmpty(), "Validation should success because the bug of bigDecimalMapper, it will treat 1.7976931348623159e+308 as INFINITY");

src/test/java/com/networknt/schema/MaximumValidatorTest.java:294

  • Filtering out "type" errors and asserting only maximum behavior can mask expected type failures for non-finite values. Prefer asserting the type error(s) or removing "type" from the schema for this scenario. Also remove the extra semicolon after toList().
        messages = v.validate(doc).stream().filter(e -> !e.getKeyword().equals("type")).toList();;
        // Before 2.16.0 messages will be empty due to bug https://github.com/FasterXML/jackson-databind/issues/1770
        // assertTrue(messages.isEmpty(), "Validation should success because the bug of bigDecimalMapper, it will treat 1.7976931348623159e+308 as INFINITY");
        assertFalse(messages.isEmpty(), "Validation should fail as Incorrect deserialization for BigDecimal numbers is fixed in 2.16.0");
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mjcarman
Copy link
Copy Markdown

mjcarman commented Apr 3, 2026

I haven't tested this, but presumably it would solve the problem that caused me to create #1239, and for that I thank you.

That said, the approach in this pull request seems to contradict your comment on that issue:

The line the spec authors seem to be saying is that you are going to need custom keywords/validators if you intend to process these as-is and you can write custom validators with this implementation so you shouldn't expect any built-in support for these values here, but you can expect that it shouldn't be a case where you can't write a custom implementation because some part of the library is throwing an exception.

If I created a custom float type which was just number + infinities + NaN and used that in my schema, I'd still have problems with things like { "type": "float", "minimum": 0 } if the input data was a NaN. With this implementation I'd have to write a custom float-minimum, float-maximum, etc. That is, perhaps, technically correct per the JSON schema specification, but as an implementation it's needlessly burdensome and inflexible.

The changes is my pull request make those validators robust with respect to infinity and NaN without limiting them to strict number semantics. You could (and probably should) make Infinity and NaN fail a (strict) type: number directly instead of delegating that to all the other validators that operate on numbers.

@justin-tay
Copy link
Copy Markdown
Collaborator Author

Don't see how it contradicts. Essentially NaN, Infinity and -Infinity don't match any JSON type. Hence if there are any type constraints eg. number, you should expect it to fail. Since these aren't numbers, the validators that work on numbers like minimum etc all skip validation like if the instance data was a string.

Essentially if you wish to add any different semantics you need to create a new set of validators for type, minimum etc, and by right need your own vocab and dialect since this is non standard. Most people just replace the keywords in the standard dialect/vocab.

This library is really an implementation of JSON Schema and the YAML support is largely to support OpenAPI and not that this library is supposed to be some generic YAML validator. Again if you disagree you should discuss your case at https://github.com/json-schema-org/json-schema-spec/issues.

@stevehu stevehu merged commit 054f2f6 into networknt:master Apr 5, 2026
3 checks passed
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

Successfully merging this pull request may close these issues.

Request: Add support for non-finite values in schema and data

4 participants