Skip to content

Commit a456ff5

Browse files
committed
-only compares with Long value when the schema type is int/long and the compare node is integer type, otherwise means one of schema or compare node is with decimal, so convert it to BigDecimal to do the compare.
-change some edge test cases, due to the way of measuring is changed, some edge scenarios don't apply anymore.
1 parent ee0b21c commit a456ff5

File tree

2 files changed

+113
-33
lines changed

2 files changed

+113
-33
lines changed

src/main/java/com/networknt/schema/MaximumValidator.java

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.slf4j.Logger;
2121
import org.slf4j.LoggerFactory;
2222

23+
import java.math.BigDecimal;
2324
import java.math.BigInteger;
2425
import java.util.Collections;
2526
import java.util.Set;
@@ -46,34 +47,17 @@ public MaximumValidator(String schemaPath, JsonNode schemaNode, JsonSchema paren
4647
}
4748

4849
parseErrorCode(getValidatorType().getErrorCodeKey());
49-
//if the schema type is not integer, or the maximum value is not an integer, try to compare using double values;
50-
if (!JsonType.INTEGER.toString().equals(getNodeFieldType()) || !JsonType.INTEGER.toString().equals(schemaNode.getNodeType())) {
51-
// "number" or no type
52-
// by default treat value as double: compatible with previous behavior
53-
final double dm = schemaNode.doubleValue();
54-
typedMaximum = new ThresholdMixin() {
55-
@Override
56-
public boolean crossesThreshold(JsonNode node) {
57-
double value = node.asDouble();
58-
return greaterThan(value, dm) || (excludeEqual && MaximumValidator.this.equals(value, dm));
59-
}
60-
61-
@Override
62-
public String thresholdValue() {
63-
return String.valueOf(dm);
64-
}
65-
};
6650

67-
} else if ( schemaNode.isLong() || schemaNode.isInt() ) {
51+
if (( schemaNode.isLong() || schemaNode.isInt() ) && (JsonType.INTEGER.toString().equals(getNodeFieldType()))) {
6852
// "integer", and within long range
6953
final long lm = schemaNode.asLong();
7054
typedMaximum = new ThresholdMixin() {
7155
@Override
7256
public boolean crossesThreshold(JsonNode node) {
7357
long val = node.asLong();
74-
if(node.isBigInteger()) {
58+
if (node.isBigInteger()) {
7559
//node.isBigInteger is not trustable, the type BigInteger doesn't mean it is a big number.
76-
if(node.bigIntegerValue().compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) {
60+
if (node.bigIntegerValue().compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) {
7761
return true;
7862
}
7963
}
@@ -85,20 +69,22 @@ public String thresholdValue() {
8569
return String.valueOf(lm);
8670
}
8771
};
88-
8972
} else {
90-
// "integer" outside long range
91-
final BigInteger bim = new BigInteger(schemaNode.asText());
9273
typedMaximum = new ThresholdMixin() {
9374
@Override
9475
public boolean crossesThreshold(JsonNode node) {
95-
int cmp = bim.compareTo(node.bigIntegerValue());
96-
return cmp < 0 || (excludeEqual && cmp <= 0);
76+
if(schemaNode.doubleValue() == Double.POSITIVE_INFINITY) {
77+
return false;
78+
}
79+
final BigDecimal max = new BigDecimal(schemaNode.asText());
80+
if(node.doubleValue() == Double.POSITIVE_INFINITY) {return true;}
81+
BigDecimal value = new BigDecimal(node.asText());
82+
return value.compareTo(max) > 0 || (excludeEqual && value.compareTo(max) == 0);
9783
}
9884

9985
@Override
10086
public String thresholdValue() {
101-
return String.valueOf(bim);
87+
return schemaNode.asText();
10288
}
10389
};
10490
}
@@ -117,5 +103,4 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
117103
}
118104
return Collections.emptySet();
119105
}
120-
121106
}

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

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public class MaximumValidatorTest {
4040
@Before
4141
public void setUp() {
4242
mapper = new ObjectMapper();
43+
// due to a jackson bug, a float number which is larger than Double.POSITIVE_INFINITY cannot be convert to BigDecimal correctly
44+
// https://github.com/FasterXML/jackson-databind/issues/1770
4345
bigDecimalMapper = new ObjectMapper().enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
4446
bigIntegerMapper = new ObjectMapper().enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
4547
}
@@ -137,14 +139,25 @@ public void negativeDoubleOverflowTest() throws IOException {
137139
assertTrue(format("Maximum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", maximum, value), messages.isEmpty());
138140

139141
// document parsed with BigDecimal
142+
140143
doc = bigDecimalMapper.readTree(value);
141-
messages = v.validate(doc);
142-
assertTrue(format("Maximum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", maximum, value), messages.isEmpty());
144+
Set<ValidationMessage> messages2 = v.validate(doc);
145+
if(Double.valueOf(maximum) == Double.POSITIVE_INFINITY) {
146+
assertTrue(format("Maximum %s and value %s are equal, thus no schema violation should be reported", maximum, value), messages2.isEmpty());
147+
} else {
148+
assertFalse(format("Maximum %s is smaller than value %s , should be validation error reported", maximum, value), messages2.isEmpty());
149+
}
150+
143151

144152
// schema and document parsed with BigDecimal
145153
v = factory.getSchema(bigDecimalMapper.readTree(schema));
146-
messages = v.validate(doc);
147-
assertTrue(format("Maximum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", maximum, value), messages.isEmpty());
154+
Set<ValidationMessage> messages3 = v.validate(doc);
155+
//when the schema and value are both using BigDecimal, the value should be parsed in same mechanism.
156+
if(maximum.toLowerCase().equals(value.toLowerCase()) || Double.valueOf(maximum) == Double.POSITIVE_INFINITY) {
157+
assertTrue(format("Maximum %s and value %s are equal, thus no schema violation should be reported", maximum, value), messages3.isEmpty());
158+
} else {
159+
assertFalse(format("Maximum %s is smaller than value %s , should be validation error reported", maximum, value), messages3.isEmpty());
160+
}
148161
}
149162
}
150163

@@ -165,7 +178,9 @@ public void doubleValueCoarsing() throws IOException {
165178

166179
doc = bigDecimalMapper.readTree(content);
167180
messages = v.validate(doc);
168-
assertTrue("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
181+
// "1.7976931348623158e+308" == "1.7976931348623157e+308" == Double.MAX_VALUE
182+
// new BigDecimal("1.7976931348623158e+308").compareTo(new BigDecimal("1.7976931348623157e+308")) > 0
183+
assertFalse("Validation should not succeed because content is using bigDecimalMapper, and bigger than the maximum", messages.isEmpty());
169184

170185
/**
171186
* Note: technically this is where 1.7976931348623158e+308 rounding to 1.7976931348623157e+308 could be spotted,
@@ -176,7 +191,7 @@ public void doubleValueCoarsing() throws IOException {
176191
*/
177192
v = factory.getSchema(bigDecimalMapper.readTree(schema));
178193
messages = v.validate(doc);
179-
assertTrue("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
194+
assertFalse("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
180195
}
181196

182197
@Test
@@ -421,6 +436,86 @@ public void testIntegerTypeWithFloatMaxNegative() throws IOException {
421436
assertFalse(format("Expecting validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
422437
}
423438
}
439+
440+
@Test
441+
public void testMaximumDoubleValue() throws IOException {
442+
String[][] values = {
443+
// maximum, value
444+
{"1E39", "1000"}
445+
};
446+
447+
for(String[] aTestCycle : values) {
448+
String maximum = aTestCycle[0];
449+
String value = aTestCycle[1];
450+
String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
451+
452+
JsonSchema v = factory.getSchema(mapper.readTree(schema));
453+
JsonNode doc = mapper.readTree(value);
454+
455+
Set<ValidationMessage> messages = v.validate(doc);
456+
assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
457+
}
458+
}
459+
460+
@Test
461+
public void testMaximumDoubleValueNegative() throws IOException {
462+
String[][] values = {
463+
// maximum, value
464+
{"1000", "1E39"}
465+
};
466+
467+
for(String[] aTestCycle : values) {
468+
String maximum = aTestCycle[0];
469+
String value = aTestCycle[1];
470+
String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
471+
472+
JsonSchema v = factory.getSchema(mapper.readTree(schema));
473+
JsonNode doc = mapper.readTree(value);
474+
475+
Set<ValidationMessage> messages = v.validate(doc);
476+
assertFalse(format("Expecting validation errors as value %s is greater than maximum %s", maximum, value), messages.isEmpty());
477+
}
478+
}
479+
480+
@Test
481+
public void testMaximumDoubleValueWithNumberType() throws IOException {
482+
String[][] values = {
483+
// maximum, value
484+
{"1000.1", "1000"}
485+
};
486+
487+
for(String[] aTestCycle : values) {
488+
String maximum = aTestCycle[0];
489+
String value = aTestCycle[1];
490+
String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
491+
492+
JsonSchema v = factory.getSchema(mapper.readTree(schema));
493+
JsonNode doc = mapper.readTree(value);
494+
495+
Set<ValidationMessage> messages = v.validate(doc);
496+
assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
497+
}
498+
}
499+
500+
@Test
501+
public void testMaximumDoubleValueWithNumberTypeNegative() throws IOException {
502+
String[][] values = {
503+
// maximum, value
504+
{"1000", "1000.1"}
505+
};
506+
507+
for(String[] aTestCycle : values) {
508+
String maximum = aTestCycle[0];
509+
String value = aTestCycle[1];
510+
String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
511+
512+
JsonSchema v = factory.getSchema(mapper.readTree(schema));
513+
JsonNode doc = mapper.readTree(value);
514+
515+
Set<ValidationMessage> messages = v.validate(doc);
516+
assertFalse(format("Expecting validation errors as value %s is greater than maximum %s", maximum, value), messages.isEmpty());
517+
}
518+
}
424519
}
425520

426521

0 commit comments

Comments
 (0)