Repository: networknt/json-schema-validator
Pull request #164 (2019-06-11 18:40:56)

Fix/#160 max value float
related issue: https://github.com/networknt/json-schema-validator/issues/160
did a performance test, it seems no big performance impact use BigDecimal to do the comparison, to use BigDecimal comparison will reduce a lot of defects.

tested with comparing with "1E39", "-1000" 200000 times after change. The average time is
2312ms
tested with comparing with "1000.1", "1000" 200000 times before change. The average time is 2326.8ms



Patch:
diff --git a/pom.xml b/pom.xml
index 59ede70..3a1404c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,7 +59,7 @@
     </distributionManagement>
     <properties>
         <java.version>1.8</java.version>
-        <java.testversion>1.7</java.testversion>
+        <java.testversion>1.8</java.testversion>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <version.jackson>2.9.9</version.jackson>
         <version.slf4j>1.7.25</version.slf4j>
diff --git a/src/main/java/com/networknt/schema/MaximumValidator.java b/src/main/java/com/networknt/schema/MaximumValidator.java
index 14e7ee8..5288fe5 100644
--- a/src/main/java/com/networknt/schema/MaximumValidator.java
+++ b/src/main/java/com/networknt/schema/MaximumValidator.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.Collections;
 import java.util.Set;
@@ -33,7 +34,7 @@ public class MaximumValidator extends BaseJsonValidator implements JsonValidator
     private final ThresholdMixin typedMaximum;
 
 
-    public MaximumValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
+    public MaximumValidator(String schemaPath, final JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.MAXIMUM, validationContext);
 
         if (!schemaNode.isNumber()) {
@@ -47,33 +48,16 @@ public class MaximumValidator extends BaseJsonValidator implements JsonValidator
 
         parseErrorCode(getValidatorType().getErrorCodeKey());
 
-        if (!JsonType.INTEGER.toString().equals(getNodeFieldType())) {
-            // "number" or no type
-            // by default treat value as double: compatible with previous behavior
-            final double dm = schemaNode.doubleValue();
-            typedMaximum = new ThresholdMixin() {
-                @Override
-                public boolean crossesThreshold(JsonNode node) {
-                    double value = node.asDouble();
-                    return greaterThan(value, dm) || (excludeEqual && MaximumValidator.this.equals(value, dm));
-                }
-
-                @Override
-                public String thresholdValue() {
-                    return String.valueOf(dm);
-                }
-            };
-
-        } else if ( schemaNode.isLong() || schemaNode.isInt() ) {
+        if (( schemaNode.isLong() || schemaNode.isInt() ) && (JsonType.INTEGER.toString().equals(getNodeFieldType()))) {
             // "integer", and within long range
             final long lm = schemaNode.asLong();
             typedMaximum = new ThresholdMixin() {
                 @Override
                 public boolean crossesThreshold(JsonNode node) {
                     long val = node.asLong();
-                    if(node.isBigInteger()) {
+                    if (node.isBigInteger()) {
                         //node.isBigInteger is not trustable, the type BigInteger doesn't mean it is a big number.
-                        if(node.bigIntegerValue().compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) {
+                        if (node.bigIntegerValue().compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0) {
                             return true;
                         }
                     }
@@ -85,20 +69,22 @@ public class MaximumValidator extends BaseJsonValidator implements JsonValidator
                     return String.valueOf(lm);
                 }
             };
-
         } else {
-            // "integer" outside long range
-            final BigInteger bim = new BigInteger(schemaNode.asText());
             typedMaximum = new ThresholdMixin() {
                 @Override
                 public boolean crossesThreshold(JsonNode node) {
-                    int cmp = bim.compareTo(node.bigIntegerValue());
-                    return cmp < 0 || (excludeEqual && cmp <= 0);
+                    if(schemaNode.doubleValue() == Double.POSITIVE_INFINITY) {
+                        return false;
+                    }
+                    final BigDecimal max = new BigDecimal(schemaNode.asText());
+                    if(node.doubleValue() == Double.POSITIVE_INFINITY) {return true;}
+                    BigDecimal value = new BigDecimal(node.asText());
+                    return value.compareTo(max) > 0 || (excludeEqual && value.compareTo(max) == 0);
                 }
 
                 @Override
                 public String thresholdValue() {
-                    return String.valueOf(bim);
+                    return schemaNode.asText();
                 }
             };
         }
@@ -117,5 +103,4 @@ public class MaximumValidator extends BaseJsonValidator implements JsonValidator
         }
         return Collections.emptySet();
     }
-
 }
\ No newline at end of file
diff --git a/src/main/java/com/networknt/schema/MinimumValidator.java b/src/main/java/com/networknt/schema/MinimumValidator.java
index 672380f..a015959 100644
--- a/src/main/java/com/networknt/schema/MinimumValidator.java
+++ b/src/main/java/com/networknt/schema/MinimumValidator.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.Collections;
 import java.util.Set;
@@ -50,24 +51,7 @@ public class MinimumValidator extends BaseJsonValidator implements JsonValidator
 
         parseErrorCode(getValidatorType().getErrorCodeKey());
 
-        if (!JsonType.INTEGER.toString().equals(getNodeFieldType())) {
-            // "number" or no type
-            // by default treat value as double: compatible with previous behavior
-            final double dmin = schemaNode.doubleValue();
-            typedMinimum = new ThresholdMixin() {
-                @Override
-                public boolean crossesThreshold(JsonNode node) {
-                    double value = node.asDouble();
-                    return lessThan(value, dmin) || (excluded && MinimumValidator.this.equals(value, dmin));
-                }
-
-                @Override
-                public String thresholdValue() {
-                    return String.valueOf(dmin);
-                }
-            };
-
-        } else if ( schemaNode.isLong() || schemaNode.isInt() ) {
+        if ( schemaNode.isLong() || schemaNode.isInt() && JsonType.INTEGER.toString().equals(getNodeFieldType())) {
             // "integer", and within long range
             final long lmin = schemaNode.asLong();
             typedMinimum = new ThresholdMixin() {
@@ -90,18 +74,21 @@ public class MinimumValidator extends BaseJsonValidator implements JsonValidator
             };
 
         } else {
-            // "integer" outside long range
-            final BigInteger bimin = new BigInteger(schemaNode.asText());
             typedMinimum = new ThresholdMixin() {
                 @Override
                 public boolean crossesThreshold(JsonNode node) {
-                    int cmp = bimin.compareTo(node.bigIntegerValue());
-                    return cmp > 0 || (excluded && cmp >= 0);
+                    if(schemaNode.doubleValue() == Double.NEGATIVE_INFINITY) {
+                        return false;
+                    }
+                    final BigDecimal min = new BigDecimal(schemaNode.asText());
+                    if(node.doubleValue() == Double.NEGATIVE_INFINITY) {return true;}
+                    BigDecimal value = new BigDecimal(node.asText());
+                    return value.compareTo(min) < 0 || (excluded && value.compareTo(min) == 0);
                 }
 
                 @Override
                 public String thresholdValue() {
-                    return String.valueOf(bimin);
+                    return schemaNode.asText();
                 }
             };
         }


Test patch:
diff --git a/src/test/java/com/networknt/schema/MaximumValidatorPerfTest.java b/src/test/java/com/networknt/schema/MaximumValidatorPerfTest.java
new file mode 100644
index 0000000..4d2b180
--- /dev/null
+++ b/src/test/java/com/networknt/schema/MaximumValidatorPerfTest.java
@@ -0,0 +1,63 @@
+package com.networknt.schema;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+@Ignore
+public class MaximumValidatorPerfTest {
+    MaximumValidatorTest test = new MaximumValidatorTest();
+
+    @Test
+    public void testTime() throws InvocationTargetException, IllegalAccessException {
+        test.setUp();
+        String[] testMethodsToBeExecuted = {"testMaximumDoubleValue"};
+        List<Method> testMethods = getTestMethods(testMethodsToBeExecuted);
+        long start = System.currentTimeMillis();
+        executeTests(testMethods, 200000);
+        long end = System.currentTimeMillis();
+        System.out.println("time to execute all tests using:" + (end - start) +"ms");
+    }
+
+    public void executeTests(List<Method> methods, int executeTimes) throws InvocationTargetException, IllegalAccessException {
+
+        for(int i = 0; i < executeTimes; i++) {
+            for(Method testMethod : methods) {
+                testMethod.invoke(test);
+            }
+        }
+
+    }
+
+    public List<Method> getTestMethods(String[] methodNames) {
+        Method[] methods = test.getClass().getMethods();
+        List<Method> testMethods = new ArrayList<Method>();
+        if(methodNames.length > 0) {
+            for(String name : methodNames) {
+                testMethods.addAll(Arrays.stream(methods).filter(m -> m.getName().equals(name)).collect(Collectors.toList()));
+            }
+            return testMethods;
+        }
+        for (Method m : methods) {
+            Annotation[] annotations = m.getDeclaredAnnotations();
+            boolean isTestMethod = false;
+            for(Annotation annotation : annotations) {
+                if(annotation.annotationType() == Test.class) {
+                    isTestMethod = true;
+                }
+            }
+            if(isTestMethod) {
+                //filter out incompatible test cases.
+                if(!m.getName().equals("doubleValueCoarsing") && !m.getName().equals("negativeDoubleOverflowTest"))
+                testMethods.add(m);
+            }
+        }
+        return testMethods;
+    }
+}
diff --git a/src/test/java/com/networknt/schema/MaximumValidatorTest.java b/src/test/java/com/networknt/schema/MaximumValidatorTest.java
index 5cdcb12..79e85a3 100644
--- a/src/test/java/com/networknt/schema/MaximumValidatorTest.java
+++ b/src/test/java/com/networknt/schema/MaximumValidatorTest.java
@@ -40,6 +40,8 @@ public class MaximumValidatorTest {
     @Before
     public void setUp() {
         mapper = new ObjectMapper();
+        // due to a jackson bug, a float number which is larger than Double.POSITIVE_INFINITY cannot be convert to BigDecimal correctly
+        // https://github.com/FasterXML/jackson-databind/issues/1770
         bigDecimalMapper = new ObjectMapper().enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
         bigIntegerMapper = new ObjectMapper().enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
     }
@@ -63,7 +65,7 @@ public class MaximumValidatorTest {
             JsonNode doc = mapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertFalse(format("Expecing validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
+            assertFalse(format("Expecting validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -86,7 +88,7 @@ public class MaximumValidatorTest {
             JsonNode doc = bigDecimalMapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertFalse(format("Expecing validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
+            assertFalse(format("Expecting validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -109,7 +111,7 @@ public class MaximumValidatorTest {
             JsonNode doc = bigDecimalMapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertFalse(format("Expecing validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
+            assertFalse(format("Expecting validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -137,14 +139,25 @@ public class MaximumValidatorTest {
             assertTrue(format("Maximum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", maximum, value), messages.isEmpty());
 
             // document parsed with BigDecimal
+
             doc = bigDecimalMapper.readTree(value);
-            messages = v.validate(doc);
-            assertTrue(format("Maximum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", maximum, value), messages.isEmpty());
+            Set<ValidationMessage> messages2 = v.validate(doc);
+            if(Double.valueOf(maximum) == Double.POSITIVE_INFINITY) {
+                assertTrue(format("Maximum %s and value %s are equal, thus no schema violation should be reported", maximum, value), messages2.isEmpty());
+            } else {
+                assertFalse(format("Maximum %s is smaller than value %s ,  should be validation error reported", maximum, value), messages2.isEmpty());
+            }
+
 
             // schema and document parsed with BigDecimal
             v = factory.getSchema(bigDecimalMapper.readTree(schema));
-            messages = v.validate(doc);
-            assertTrue(format("Maximum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", maximum, value), messages.isEmpty());
+            Set<ValidationMessage> messages3 = v.validate(doc);
+            //when the schema and value are both using BigDecimal, the value should be parsed in same mechanism.
+            if(maximum.toLowerCase().equals(value.toLowerCase()) || Double.valueOf(maximum) == Double.POSITIVE_INFINITY) {
+                assertTrue(format("Maximum %s and value %s are equal, thus no schema violation should be reported", maximum, value), messages3.isEmpty());
+            } else {
+                assertFalse(format("Maximum %s is smaller than value %s ,  should be validation error reported", maximum, value), messages3.isEmpty());
+            }
         }
     }
 
@@ -165,7 +178,9 @@ public class MaximumValidatorTest {
 
         doc = bigDecimalMapper.readTree(content);
         messages = v.validate(doc);
-        assertTrue("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
+        // "1.7976931348623158e+308" == "1.7976931348623157e+308" == Double.MAX_VALUE
+        // new BigDecimal("1.7976931348623158e+308").compareTo(new BigDecimal("1.7976931348623157e+308")) > 0
+        assertFalse("Validation should not succeed because content is using bigDecimalMapper, and bigger than the maximum", messages.isEmpty());
 
         /**
          * Note: technically this is where 1.7976931348623158e+308 rounding to 1.7976931348623157e+308 could be spotted,
@@ -176,7 +191,39 @@ public class MaximumValidatorTest {
          */
         v = factory.getSchema(bigDecimalMapper.readTree(schema));
         messages = v.validate(doc);
+        assertFalse("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
+    }
+
+    /**
+     * BigDecimalMapper issue, it doesn't work as expected, it will treat 1.7976931348623159e+308 as INFINITY instead of as it is.
+     */
+    @Test
+    public void doubleValueCoarsingExceedRange() throws IOException {
+        String schema = "{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"maximum\": 1.7976931348623159e+308 }";
+        String content = "1.7976931348623160e+308";
+
+        JsonNode doc = mapper.readTree(content);
+        JsonSchema v = factory.getSchema(mapper.readTree(schema));
+
+        Set<ValidationMessage> messages = v.validate(doc);
         assertTrue("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
+
+        doc = bigDecimalMapper.readTree(content);
+        messages = v.validate(doc);
+        // "1.7976931348623158e+308" == "1.7976931348623157e+308" == Double.MAX_VALUE
+        // new BigDecimal("1.7976931348623158e+308").compareTo(new BigDecimal("1.7976931348623157e+308")) > 0
+        assertTrue("Validation should success because the bug of bigDecimalMapper, it will treat 1.7976931348623159e+308 as INFINITY", messages.isEmpty());
+
+        /**
+         * Note: technically this is where 1.7976931348623158e+308 rounding to 1.7976931348623157e+308 could be spotted,
+         *       yet it requires a dedicated case of comparison BigDecimal to BigDecimal. Since values above
+         *       1.7976931348623158e+308 are parsed as Infinity anyways (jackson uses double as primary type with later
+         *       "upcasting" to BigDecimal, if property is set) adding a dedicated code block just for this one case
+         *       seems infeasible.
+         */
+        v = factory.getSchema(bigDecimalMapper.readTree(schema));
+        messages = v.validate(doc);
+        assertTrue("Validation should success because the bug of bigDecimalMapper, it will treat 1.7976931348623159e+308 as INFINITY", messages.isEmpty());
     }
 
     @Test
@@ -198,7 +245,7 @@ public class MaximumValidatorTest {
             JsonNode doc = mapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertFalse(format("Expecing validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
+            assertFalse(format("Expecting validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -218,7 +265,7 @@ public class MaximumValidatorTest {
             JsonNode doc = mapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -238,7 +285,7 @@ public class MaximumValidatorTest {
             JsonNode doc = bigIntegerMapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -298,7 +345,7 @@ public class MaximumValidatorTest {
             JsonNode doc = bigIntegerMapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -318,7 +365,7 @@ public class MaximumValidatorTest {
             JsonNode doc = bigIntegerMapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -358,7 +405,7 @@ public class MaximumValidatorTest {
             JsonNode doc = bigIntegerMapper.readTree(value);
 
             Set<ValidationMessage> messages = v.validate(doc);
-            assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
 
@@ -377,10 +424,130 @@ public class MaximumValidatorTest {
             JsonSchema v = factory.getSchema(mapper.readTree(schema));
             JsonNode doc = bigIntegerMapper.readTree(value);
 
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testIntegerTypeWithFloatMaxPositive() throws IOException {
+        String[][] values = {
+//            maximum,                       value
+                {"37.7",         "37"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String maximum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = bigIntegerMapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testIntegerTypeWithFloatMaxNegative() throws IOException {
+        String[][] values = {
+//            maximum,                       value
+                {"37.7",         "38"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String maximum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = bigIntegerMapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertFalse(format("Expecting validation error with maximum %s and value %s", maximum, value), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testMaximumDoubleValue() throws IOException {
+        String[][] values = {
+//            maximum,                       value
+                {"1E39",         "1000"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String maximum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
             Set<ValidationMessage> messages = v.validate(doc);
             assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
+
+    @Test
+    public void testMaximumDoubleValueNegative() throws IOException {
+        String[][] values = {
+//            maximum,                       value
+                {"1000",         "1E39"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String maximum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertFalse(format("Expecting  validation errors as value %s is greater than maximum %s", maximum, value), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testMaximumDoubleValueWithNumberType() throws IOException {
+        String[][] values = {
+//            maximum,                       value
+                {"1000.1",         "1000"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String maximum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertTrue(format("Expecting no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testMaximumDoubleValueWithNumberTypeNegative() throws IOException {
+        String[][] values = {
+//            maximum,                       value
+                {"1000",         "1000.1"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String maximum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"maximum\": %s, \"exclusiveMaximum\": false}", maximum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertFalse(format("Expecting  validation errors as value %s is greater than maximum %s", maximum, value), messages.isEmpty());
+        }
+    }
 }
 
 
diff --git a/src/test/java/com/networknt/schema/MinimumValidatorTest.java b/src/test/java/com/networknt/schema/MinimumValidatorTest.java
index a197f84..2410fae 100644
--- a/src/test/java/com/networknt/schema/MinimumValidatorTest.java
+++ b/src/test/java/com/networknt/schema/MinimumValidatorTest.java
@@ -139,13 +139,28 @@ public class MinimumValidatorTest {
 
             // document parsed with BigDecimal
             doc = bigDecimalMapper.readTree(value);
-            messages = v.validate(doc);
-            assertTrue(format("Minimum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", minimum, value), messages.isEmpty());
+            Set<ValidationMessage> messages2 = v.validate(doc);
+
+            //when the schema and value are both using BigDecimal, the value should be parsed in same mechanism.
+            if(Double.valueOf(minimum) == Double.NEGATIVE_INFINITY) {
+                /**
+                 * {"-1.000000000000000000000001E+308", "-1.000000000000000000000001E+308"} will be false
+                 * because the different between two mappers, without using big decimal, it loses some precises.
+                 */
+                assertTrue(format("Minimum %s and value %s are equal, thus no schema violation should be reported", minimum, value), messages2.isEmpty());
+            } else {
+                assertFalse(format("Minimum %s is larger than value %s ,  should be validation error reported", minimum, value), messages2.isEmpty());
+            }
 
             // schema and document parsed with BigDecimal
             v = factory.getSchema(bigDecimalMapper.readTree(schema));
-            messages = v.validate(doc);
-            assertTrue(format("Minimum %s and value %s are interpreted as Infinity, thus no schema violation should be reported", minimum, value), messages.isEmpty());
+            Set<ValidationMessage> messages3 = v.validate(doc);
+            //when the schema and value are both using BigDecimal, the value should be parsed in same mechanism.
+            if(minimum.toLowerCase().equals(value.toLowerCase()) || Double.valueOf(minimum) == Double.NEGATIVE_INFINITY) {
+                assertTrue(format("Minimum %s and value %s are equal, thus no schema violation should be reported", minimum, value), messages3.isEmpty());
+            } else {
+                assertFalse(format("Minimum %s is larger than value %s ,  should be validation error reported", minimum, value), messages3.isEmpty());
+            }
         }
     }
 
@@ -166,7 +181,7 @@ public class MinimumValidatorTest {
 
         doc = bigDecimalMapper.readTree(content);
         messages = v.validate(doc);
-        assertTrue("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
+        assertFalse("Validation should not succeed because content is using bigDecimalMapper, and smaller than the minimum", messages.isEmpty());
 
         /**
          * Note: technically this is where -1.7976931348623158e+308 rounding to -1.7976931348623157e+308 could be
@@ -177,7 +192,30 @@ public class MinimumValidatorTest {
          */
         v = factory.getSchema(bigDecimalMapper.readTree(schema));
         messages = v.validate(doc);
+        assertFalse("Validation should not succeed because content is using bigDecimalMapper, and smaller than the minimum", messages.isEmpty());
+    }
+
+    /**
+     * BigDecimalMapper issue, it doesn't work as expected, it will treat -1.7976931348623157e+309 as INFINITY instead of as it is.
+     */
+    @Test
+    public void doubleValueCoarsingExceedRange() throws IOException {
+        String schema = "{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"minimum\": -1.7976931348623159e+308 }";
+        String content = "-1.7976931348623160e+308";
+
+        JsonNode doc = mapper.readTree(content);
+        JsonSchema v = factory.getSchema(mapper.readTree(schema));
+
+        Set<ValidationMessage> messages = v.validate(doc);
         assertTrue("Validation should succeed as by default double values are used by mapper", messages.isEmpty());
+
+        doc = bigDecimalMapper.readTree(content);
+        messages = v.validate(doc);
+        assertTrue("Validation should succeed due to the bug of BigDecimal option of mapper", messages.isEmpty());
+
+        v = factory.getSchema(bigDecimalMapper.readTree(schema));
+        messages = v.validate(doc);
+        assertTrue("Validation should succeed due to the bug of BigDecimal option of mapper", messages.isEmpty());
     }
 
     @Test
@@ -381,6 +419,86 @@ public class MinimumValidatorTest {
             assertTrue(format("Expecing no validation errors as maximum %s is greater than value %s", maximum, value), messages.isEmpty());
         }
     }
+
+    @Test
+    public void testMinimumDoubleValue() throws IOException {
+        String[][] values = {
+//            minimum,                       value
+                {"-1E309",         "-1000"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String minimum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"minimum\": %s, \"exclusiveMinimum\": false}", minimum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertTrue(format("Expecting no validation errors as value %s is greater than minimum %s", value, minimum), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testMinimumDoubleValueNegative() throws IOException {
+        String[][] values = {
+//            minimum,                       value
+                {"-1000",         "-1E309"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String minimum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\", \"minimum\": %s, \"exclusiveMinimum\": false}", minimum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertFalse(format("Expecting  validation errors as value %s is smaller than minimum %s", value, minimum), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testMinimumDoubleValueWithNumberType() throws IOException {
+        String[][] values = {
+//            minimum,                       value
+                {"1000",         "1000.1"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String minimum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"minimum\": %s, \"exclusiveMinimum\": false}", minimum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertTrue(format("Expecting no validation errors as value %s is greater than minimum %s", value, minimum), messages.isEmpty());
+        }
+    }
+
+    @Test
+    public void testMinimumDoubleValueWithNumberTypeNegative() throws IOException {
+        String[][] values = {
+//            minimum,                       value
+                {"1000.1",         "1000"}
+        };
+
+        for(String[] aTestCycle : values) {
+            String minimum = aTestCycle[0];
+            String value = aTestCycle[1];
+            String schema = format("{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"minimum\": %s, \"exclusiveMinimum\": false}", minimum);
+
+            JsonSchema v = factory.getSchema(mapper.readTree(schema));
+            JsonNode doc = mapper.readTree(value);
+
+            Set<ValidationMessage> messages = v.validate(doc);
+            assertFalse(format("Expecting  validation errors as value %s is smaller than minimum %s", value, minimum), messages.isEmpty());
+        }
+    }
 }
 
 
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
index bfcfd9f..d61b04f 100644
--- a/src/test/resources/logback-test.xml
+++ b/src/test/resources/logback-test.xml
@@ -1,31 +1,31 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright (c) 2016 Network New Technologies Inc.
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<configuration>
-
-    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-        <!-- encoders are assigned the type
-             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
-        <encoder>
-            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
-        </encoder>
-    </appender>
-
-    <root level="DEBUG">
-        <appender-ref ref="STDOUT"/>
-    </root>
-</configuration>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2016 Network New Technologies Inc.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!-- encoders are assigned the type
+             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="ERROR">
+        <appender-ref ref="STDOUT"/>
+    </root>
+</configuration>