Repository: networknt/json-schema-validator
Pull request #1065 (2024-06-14 01:53:13)

Adds JsonNodeReader
Adds an `JsonNodeReader` interface to abstract out the logic for reading content into a `JsonNode`.

This will make it easier to customize how the reading is done with a custom implementation. This also potentially allows easier reuse of the logic of reading location aware nodes by trying to decouple this from the `JsonSchemaFactory`.

This deprecates the setting of `jsonMapper` and `yamlMapper` on the `JsonSchemaFactory` in favor of the `jsonNodeReader`.

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt) Report
Attention: Patch coverage is `64.28571%` with `15 lines` in your changes missing coverage. Please review.
> Project coverage is 78.68%. Comparing base [(`48ca3c2`)](https://app.codecov.io/gh/networknt/json-schema-validator/commit/48ca3c22995e32797e9defdb8b77869f0d15caf3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt) to head [(`82b67b5`)](https://app.codecov.io/gh/networknt/json-schema-validator/commit/82b67b5488efaede6a16b0115cf5efc270355c39?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt).
> Report is 31 commits behind head on master.

| [Files](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt) | Patch % | Lines |
|---|---|---|
| [...rknt/schema/serialization/DefaultObjectReader.java](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fcom%2Fnetworknt%2Fschema%2Fserialization%2FDefaultObjectReader.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt#diff-c3JjL21haW4vamF2YS9jb20vbmV0d29ya250L3NjaGVtYS9zZXJpYWxpemF0aW9uL0RlZmF1bHRPYmplY3RSZWFkZXIuamF2YQ==) | 60.60% | [9 Missing and 4 partials :warning: ](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt) |
| [...n/java/com/networknt/schema/JsonSchemaFactory.java](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fcom%2Fnetworknt%2Fschema%2FJsonSchemaFactory.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt#diff-c3JjL21haW4vamF2YS9jb20vbmV0d29ya250L3NjaGVtYS9Kc29uU2NoZW1hRmFjdG9yeS5qYXZh) | 75.00% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt) |

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##             master    #1065      +/-   ##
============================================
- Coverage     78.90%   78.68%   -0.22%     
- Complexity     1965     2053      +88     
============================================
  Files           172      198      +26     
  Lines          6352     6635     +283     
  Branches       1255     1249       -6     
============================================
+ Hits           5012     5221     +209     
- Misses          867      936      +69     
- Partials        473      478       +5     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/networknt/json-schema-validator/pull/1065?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=networknt).


Patch:
diff --git a/README.md b/README.md
index cfb0f92..7ac24f8 100644
--- a/README.md
+++ b/README.md
@@ -353,8 +353,8 @@ Assertions contains the following additional information
 | Arguments         | The arguments used for generating the message.
 | Type              | The keyword that generated the message.
 | Property          | The property name that caused the validation error for example for the `required` keyword. Note that this is not part of the instance location as that points to the instance node.
-| Schema Node       | The `JsonNode` pointed to by the Schema Location. This is the schema data that caused the input data to fail. It is possible to get the location information by configuring the `JsonSchemaFactory` with the `LocationJsonNodeFactoryFactory` and using `JsonNodes.tokenLocationOf(schemaNode)`.
-| Instance Node     | The `JsonNode` pointed to by the Instance Location. This is the input data that failed validation. It is possible to get the location information by configuring the `JsonSchemaFactory` with the `LocationJsonNodeFactoryFactory` and using `JsonNodes.tokenLocationOf(instanceNode)`.
+| Schema Node       | The `JsonNode` pointed to by the Schema Location. This is the schema data that caused the input data to fail. It is possible to get the location information by configuring the `JsonSchemaFactory` with a `JsonNodeReader` that uses the `LocationJsonNodeFactoryFactory` and using `JsonNodes.tokenLocationOf(schemaNode)`.
+| Instance Node     | The `JsonNode` pointed to by the Instance Location. This is the input data that failed validation. It is possible to get the location information by configuring the `JsonSchemaFactory` with a `JsonNodeReader` that uses the `LocationJsonNodeFactoryFactory` and using `JsonNodes.tokenLocationOf(instanceNode)`.
 | Details           | Additional details that can be set by custom keyword validator implementations. This is not used by the library.
 
 Annotations contains the following additional information
@@ -367,7 +367,7 @@ Annotations contains the following additional information
 
 The library can be configured to store line and column information in the `JsonNode` instances for the instance and schema nodes. This will adversely affect performance and is not configured by default.
 
-This is done by configuring a `LocationJsonNodeFactoryFactory` on the `JsonSchemaFactory`. The `JsonLocation` information can then be retrieved using `JsonNodes.tokenLocationOf(jsonNode)`.
+This is done by configuring a `JsonNodeReader` that uses the `LocationJsonNodeFactoryFactory`on the `JsonSchemaFactory`. The `JsonLocation` information can then be retrieved using `JsonNodes.tokenLocationOf(jsonNode)`.
 
 ```java
 String schemaData = "{\r\n"
@@ -383,7 +383,7 @@ String inputData = "{\r\n"
                 + "  \"startDate\": \"1\"\r\n"
                 + "}";
 JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012,
-        builder -> builder.jsonNodeFactoryFactory(LocationJsonNodeFactoryFactory.getInstance()));
+        builder -> builder.jsonNodeReader(JsonNodeReader.builder().locationAware().build()));
 SchemaValidatorsConfig config = new SchemaValidatorsConfig();
 config.setPathType(PathType.JSON_POINTER);
 JsonSchema schema = factory.getSchema(schemaData, InputFormat.JSON, config);
diff --git a/src/main/java/com/networknt/schema/DisallowUnknownJsonMetaSchemaFactory.java b/src/main/java/com/networknt/schema/DisallowUnknownJsonMetaSchemaFactory.java
index 938f424..6ca259b 100644
--- a/src/main/java/com/networknt/schema/DisallowUnknownJsonMetaSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/DisallowUnknownJsonMetaSchemaFactory.java
@@ -31,6 +31,11 @@ public class DisallowUnknownJsonMetaSchemaFactory implements JsonMetaSchemaFacto
         private static DisallowUnknownJsonMetaSchemaFactory INSTANCE = new DisallowUnknownJsonMetaSchemaFactory();
     }
 
+    /**
+     * Gets the instance of {@link DisallowUnknownJsonMetaSchemaFactory}.
+     * 
+     * @return the json meta schema factory
+     */
     public static DisallowUnknownJsonMetaSchemaFactory getInstance() {
         return Holder.INSTANCE;
     }
diff --git a/src/main/java/com/networknt/schema/DisallowUnknownKeywordFactory.java b/src/main/java/com/networknt/schema/DisallowUnknownKeywordFactory.java
index 3a66505..e0f3f7e 100644
--- a/src/main/java/com/networknt/schema/DisallowUnknownKeywordFactory.java
+++ b/src/main/java/com/networknt/schema/DisallowUnknownKeywordFactory.java
@@ -36,6 +36,11 @@ public class DisallowUnknownKeywordFactory implements KeywordFactory {
         private static DisallowUnknownKeywordFactory INSTANCE = new DisallowUnknownKeywordFactory();
     }
 
+    /**
+     * Gets the instance of {@link DisallowUnknownKeywordFactory}.
+     *
+     * @return the keyword factory
+     */
     public static DisallowUnknownKeywordFactory getInstance() {
         return Holder.INSTANCE;
     }
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index f1fbdbe..1c3182f 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -1021,6 +1021,7 @@ public class JsonSchema extends BaseJsonValidator {
     /**
      * Walk the JSON node.
      * 
+     * @param <T>         the result type
      * @param executionContext the execution context
      * @param node             the input
      * @param outputFormat     the output format
@@ -1053,6 +1054,7 @@ public class JsonSchema extends BaseJsonValidator {
     /**
      * Walk the JSON node.
      * 
+     * @param <T>         the result type
      * @param executionContext the execution context
      * @param node             the input
      * @param outputFormat     the output format
@@ -1099,6 +1101,7 @@ public class JsonSchema extends BaseJsonValidator {
     /**
      * Walk the input.
      * 
+     * @param <T>         the result type
      * @param executionContext the execution context
      * @param input            the input
      * @param inputFormat      the input format
@@ -1132,6 +1135,7 @@ public class JsonSchema extends BaseJsonValidator {
     /**
      * Walk the input.
      * 
+     * @param <T>         the result type
      * @param executionContext the execution context
      * @param input            the input
      * @param inputFormat      the input format
@@ -1160,6 +1164,7 @@ public class JsonSchema extends BaseJsonValidator {
     /**
      * Walk the JSON node.
      * 
+     * @param <T>         the result type
      * @param node     the input
      * @param validate true to validate the input against the schema
      * @param outputFormat the output format
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index ce8363d..89ddffd 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -25,9 +25,8 @@ import com.networknt.schema.resource.SchemaLoaders;
 import com.networknt.schema.resource.SchemaMapper;
 import com.networknt.schema.resource.SchemaMappers;
 import com.networknt.schema.serialization.JsonMapperFactory;
+import com.networknt.schema.serialization.JsonNodeReader;
 import com.networknt.schema.serialization.YamlMapperFactory;
-import com.networknt.schema.serialization.node.JsonNodeFactoryFactory;
-import com.networknt.schema.utils.JsonNodes;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -56,7 +55,7 @@ public class JsonSchemaFactory {
     public static class Builder {
         private ObjectMapper jsonMapper = null;
         private ObjectMapper yamlMapper = null;
-        private JsonNodeFactoryFactory jsonNodeFactoryFactory = null;
+        private JsonNodeReader jsonNodeReader = null;
         private String defaultMetaSchemaIri;
         private final ConcurrentMap<String, JsonMetaSchema> metaSchemas = new ConcurrentHashMap<String, JsonMetaSchema>();
         private SchemaLoaders.Builder schemaLoadersBuilder = null;
@@ -65,25 +64,47 @@ public class JsonSchemaFactory {
         private JsonMetaSchemaFactory metaSchemaFactory = null;
 
         /**
-         * Configures the {@link JsonNodeFactoryFactory} to use.
+         * Sets the json node reader to read the data.
          * <p>
-         * To get location information from {@link JsonNode} the
-         * {@link com.networknt.schema.serialization.node.LocationJsonNodeFactoryFactory}
-         * can be used.
+         * If set this takes precedence over the configured json mapper and yaml mapper.
+         * <p>
+         * A location aware object reader can be created using JsonNodeReader.builder().locationAware().build().
          *
-         * @param jsonNodeFactoryFactory the factory to create json node factories
+         * @param jsonNodeReader the object reader
          * @return the builder
          */
-        public Builder jsonNodeFactoryFactory(JsonNodeFactoryFactory jsonNodeFactoryFactory) {
-            this.jsonNodeFactoryFactory = jsonNodeFactoryFactory;
+        public Builder jsonNodeReader(JsonNodeReader jsonNodeReader) {
+            this.jsonNodeReader = jsonNodeReader;
             return this;
         }
 
+        /**
+         * Sets the json mapper to read the data.
+         * <p>
+         * If the object reader is set this will not be used.
+         * <p>
+         * This is deprecated use a object reader instead.
+         * 
+         * @param jsonMapper the json mapper
+         * @return the builder
+         */
+        @Deprecated
         public Builder jsonMapper(final ObjectMapper jsonMapper) {
             this.jsonMapper = jsonMapper;
             return this;
         }
 
+        /**
+         * Sets the yaml mapper to read the data.
+         * <p>
+         * If the object reader is set this will not be used.
+         * <p>
+         * This is deprecated use a object reader instead.
+         * 
+         * @param yamlMapper the yaml mapper
+         * @return the builder
+         */
+        @Deprecated
         public Builder yamlMapper(final ObjectMapper yamlMapper) {
             this.yamlMapper = yamlMapper;
             return this;
@@ -151,7 +172,7 @@ public class JsonSchemaFactory {
             return new JsonSchemaFactory(
                     jsonMapper,
                     yamlMapper,
-                    jsonNodeFactoryFactory,
+                    jsonNodeReader,
                     defaultMetaSchemaIri,
                     schemaLoadersBuilder,
                     schemaMappersBuilder,
@@ -164,7 +185,7 @@ public class JsonSchemaFactory {
 
     private final ObjectMapper jsonMapper;
     private final ObjectMapper yamlMapper;
-    private final JsonNodeFactoryFactory jsonNodeFactoryFactory;
+    private final JsonNodeReader jsonNodeReader;
     private final String defaultMetaSchemaIri;
     private final SchemaLoaders.Builder schemaLoadersBuilder;
     private final SchemaMappers.Builder schemaMappersBuilder;
@@ -180,7 +201,7 @@ public class JsonSchemaFactory {
     private JsonSchemaFactory(
             ObjectMapper jsonMapper,
             ObjectMapper yamlMapper,
-            JsonNodeFactoryFactory jsonNodeFactoryFactory,
+            JsonNodeReader jsonNodeReader,
             String defaultMetaSchemaIri,
             SchemaLoaders.Builder schemaLoadersBuilder,
             SchemaMappers.Builder schemaMappersBuilder,
@@ -197,7 +218,7 @@ public class JsonSchemaFactory {
         }
         this.jsonMapper = jsonMapper;
         this.yamlMapper = yamlMapper;
-        this.jsonNodeFactoryFactory = jsonNodeFactoryFactory;
+        this.jsonNodeReader = jsonNodeReader;
         this.defaultMetaSchemaIri = defaultMetaSchemaIri;
         this.schemaLoadersBuilder = schemaLoadersBuilder;
         this.schemaMappersBuilder = schemaMappersBuilder;
@@ -290,7 +311,7 @@ public class JsonSchemaFactory {
                 .defaultMetaSchemaIri(blueprint.defaultMetaSchemaIri)
                 .jsonMapper(blueprint.jsonMapper)
                 .yamlMapper(blueprint.yamlMapper)
-                .jsonNodeFactoryFactory(blueprint.jsonNodeFactoryFactory);
+                .jsonNodeReader(blueprint.jsonNodeReader);
         if (blueprint.schemaLoadersBuilder != null) {
             builder.schemaLoadersBuilder = SchemaLoaders.builder().with(blueprint.schemaLoadersBuilder);
         }
@@ -442,18 +463,18 @@ public class JsonSchemaFactory {
     }
 
     JsonNode readTree(String content, InputFormat inputFormat) throws IOException {
-        if (this.jsonNodeFactoryFactory == null) {
+        if (this.jsonNodeReader == null) {
             return getObjectMapper(inputFormat).readTree(content);
         } else {
-            return JsonNodes.readTree(getObjectMapper(inputFormat), content, this.jsonNodeFactoryFactory);
+            return this.jsonNodeReader.readTree(content, inputFormat);
         }
     }
 
     JsonNode readTree(InputStream content, InputFormat inputFormat) throws IOException {
-        if (this.jsonNodeFactoryFactory == null) {
+        if (this.jsonNodeReader == null) {
             return getObjectMapper(inputFormat).readTree(content);
         } else {
-            return JsonNodes.readTree(getObjectMapper(inputFormat), content, this.jsonNodeFactoryFactory);
+            return this.jsonNodeReader.readTree(content, inputFormat);
         }
     }
 
@@ -627,10 +648,6 @@ public class JsonSchemaFactory {
         return this.jsonMapper != null ? this.jsonMapper : JsonMapperFactory.getInstance();
     }
 
-    JsonNodeFactoryFactory getJsonNodeFactoryFactory() {
-        return this.jsonNodeFactoryFactory;
-    }
-
     /**
      * Creates a schema validators config.
      * 
diff --git a/src/main/java/com/networknt/schema/serialization/DefaultJsonNodeReader.java b/src/main/java/com/networknt/schema/serialization/DefaultJsonNodeReader.java
new file mode 100644
index 0000000..10707ed
--- /dev/null
+++ b/src/main/java/com/networknt/schema/serialization/DefaultJsonNodeReader.java
@@ -0,0 +1,173 @@
+package com.networknt.schema.serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.serialization.node.JsonNodeFactoryFactory;
+import com.networknt.schema.serialization.node.LocationJsonNodeFactoryFactory;
+import com.networknt.schema.utils.JsonNodes;
+
+/**
+ * Default {@link JsonNodeReader}.
+ */
+public class DefaultJsonNodeReader implements JsonNodeReader {
+    protected final ObjectMapper jsonMapper;
+    protected final ObjectMapper yamlMapper;
+    protected final JsonNodeFactoryFactory jsonNodeFactoryFactory;
+
+    /**
+     * Constructor.
+     *
+     * @param jsonMapper the json mapper
+     * @param yamlMapper the yaml mapper
+     * @param jsonNodeFactoryFactory the json node factory factory
+     */
+    protected DefaultJsonNodeReader(ObjectMapper jsonMapper, ObjectMapper yamlMapper,
+            JsonNodeFactoryFactory jsonNodeFactoryFactory) {
+        this.jsonMapper = jsonMapper;
+        this.yamlMapper = yamlMapper;
+        this.jsonNodeFactoryFactory = jsonNodeFactoryFactory;
+    }
+
+    @Override
+    public JsonNode readTree(String content, InputFormat inputFormat) throws IOException {
+        if (this.jsonNodeFactoryFactory == null) {
+            return getObjectMapper(inputFormat).readTree(content);
+        } else {
+            return JsonNodes.readTree(getObjectMapper(inputFormat), content, this.jsonNodeFactoryFactory);
+        }
+    }
+
+    @Override
+    public JsonNode readTree(InputStream content, InputFormat inputFormat) throws IOException {
+        if (this.jsonNodeFactoryFactory == null) {
+            return getObjectMapper(inputFormat).readTree(content);
+        } else {
+            return JsonNodes.readTree(getObjectMapper(inputFormat), content, this.jsonNodeFactoryFactory);
+        }
+    }
+
+    /**
+     * Gets the yaml mapper.
+     * 
+     * @return the yaml mapper
+     */
+    protected ObjectMapper getYamlMapper() {
+        return this.yamlMapper != null ? this.yamlMapper : YamlMapperFactory.getInstance();
+    }
+
+    /**
+     * Gets the json mapper.
+     * 
+     * @return the json mapper
+     */
+    protected ObjectMapper getJsonMapper() {
+        return this.jsonMapper != null ? this.jsonMapper : JsonMapperFactory.getInstance();
+    }
+
+    /**
+     * Gets the object mapper for the input format.
+     * 
+     * @param inputFormat the input format
+     * @return the object mapper
+     */
+    protected ObjectMapper getObjectMapper(InputFormat inputFormat) {
+        if (InputFormat.JSON.equals(inputFormat)) {
+            return getJsonMapper();
+        } else if (InputFormat.YAML.equals(inputFormat)) {
+            return getYamlMapper();
+        }
+        throw new IllegalArgumentException("Unsupported input format "+inputFormat); 
+    }
+
+    /**
+     * Gets the builder for {@link DefaultJsonNodeReader}.
+     * 
+     * @return the builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder support for {@link JsonNodeReader}.
+     * 
+     * @param <T> the super type
+     */
+    public static abstract class BuilderSupport<T> {
+        protected ObjectMapper jsonMapper = null;
+        protected ObjectMapper yamlMapper = null;
+        protected JsonNodeFactoryFactory jsonNodeFactoryFactory = null;
+
+        protected abstract T self();
+
+        /**
+         * Sets the json mapper. 
+         *
+         * @param jsonMapper the json mapper
+         * @return the builder
+         */
+        public T jsonMapper(ObjectMapper jsonMapper) {
+            this.jsonMapper = jsonMapper;
+            return self();
+        }
+
+        /**
+         * Sets the yaml mapper
+         * 
+         * @param yamlMapper the yaml mapper
+         * @return the builder
+         */
+        public T yamlMapper(ObjectMapper yamlMapper) {
+            this.yamlMapper = yamlMapper;
+            return self();
+        }
+
+        /**
+         * Configures the {@link JsonNodeFactoryFactory} to use.
+         * <p>
+         * To get location information from {@link JsonNode} the
+         * {@link com.networknt.schema.serialization.node.LocationJsonNodeFactoryFactory}
+         * can be used.
+         *
+         * @param jsonNodeFactoryFactory the factory to create json node factories
+         * @return the builder
+         */
+        public T jsonNodeFactoryFactory(JsonNodeFactoryFactory jsonNodeFactoryFactory) {
+            this.jsonNodeFactoryFactory = jsonNodeFactoryFactory;
+            return self();
+        }
+    }
+
+    /**
+     * Builder for {@link DefaultJsonNodeReader}. 
+     */
+    public static class Builder extends BuilderSupport<Builder> {
+
+        @Override
+        protected Builder self() {
+            return this;
+        }
+
+        /**
+         * Makes the nodes generated location aware.
+         *
+         * @return the builder
+         */
+        public Builder locationAware() {
+            return jsonNodeFactoryFactory(LocationJsonNodeFactoryFactory.getInstance());
+        }
+
+        /**
+         * Builds the {@link JsonNodeReader}.
+         *
+         * @return the object reader
+         */
+        public JsonNodeReader build() {
+            return new DefaultJsonNodeReader(this.jsonMapper, this.yamlMapper, this.jsonNodeFactoryFactory);
+        }
+    }
+}
diff --git a/src/main/java/com/networknt/schema/serialization/JsonNodeReader.java b/src/main/java/com/networknt/schema/serialization/JsonNodeReader.java
new file mode 100644
index 0000000..25e6077
--- /dev/null
+++ b/src/main/java/com/networknt/schema/serialization/JsonNodeReader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * 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.
+ */
+package com.networknt.schema.serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.InputFormat;
+
+/**
+ * Reader for reading content to {@link JsonNode}.
+ */
+public interface JsonNodeReader {
+
+    /**
+     * Deserialize content as a tree.
+     * 
+     * @param content     the content
+     * @param inputFormat the input format
+     * @return the node
+     * @throws IOException
+     */
+    JsonNode readTree(String content, InputFormat inputFormat) throws IOException;
+
+    /**
+     * Deserialize content as a tree.
+     * 
+     * @param content
+     * @param inputFormat
+     * @return the node
+     * @throws IOException
+     */
+    JsonNode readTree(InputStream content, InputFormat inputFormat) throws IOException;
+
+    /**
+     * Creates a builder for {@link JsonNodeReader}.
+     *
+     * @return the builder
+     */
+    public static DefaultJsonNodeReader.Builder builder() {
+        return DefaultJsonNodeReader.builder();
+    }
+}


Test patch:
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index 4e9e8f1..cc066e9 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -16,11 +16,9 @@
 
 package com.networknt.schema;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.networknt.schema.SpecVersion.VersionFlag;
 import com.networknt.schema.regex.JDKRegularExpressionFactory;
 import com.networknt.schema.regex.JoniRegularExpressionFactory;
-import com.networknt.schema.serialization.JsonMapperFactory;
 import com.networknt.schema.suite.TestCase;
 import com.networknt.schema.suite.TestSource;
 import com.networknt.schema.suite.TestSpec;
@@ -48,9 +46,6 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest;
 
 public abstract class AbstractJsonSchemaTestSuite extends HTTPServiceSupport {
 
-
-    protected ObjectMapper mapper = JsonMapperFactory.getInstance();
-
     private static String toForwardSlashPath(Path file) {
         return file.toString().replace('\\', '/');
     }
@@ -190,7 +185,6 @@ public abstract class AbstractJsonSchemaTestSuite extends HTTPServiceSupport {
         JsonSchemaFactory base = JsonSchemaFactory.getInstance(specVersion);
         return JsonSchemaFactory
                 .builder(base)
-                .jsonMapper(this.mapper)
                 .schemaMappers(schemaMappers -> schemaMappers
                         .mapPrefix("https://", "http://")
                         .mapPrefix("http://json-schema.org", "resource:"))
diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java
index ce77eee..a5d98a2 100644
--- a/src/test/java/com/networknt/schema/Issue285Test.java
+++ b/src/test/java/com/networknt/schema/Issue285Test.java
@@ -15,7 +15,6 @@ public class Issue285Test {
     private ObjectMapper mapper = new ObjectMapper();
     private JsonSchemaFactory schemaFactory = JsonSchemaFactory
 		.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
-		.jsonMapper(mapper)
             .schemaMappers(schemaMappers -> schemaMappers
                     .mapPrefix("http://json-schema.org", "resource:")
                     .mapPrefix("https://json-schema.org", "resource:"))
diff --git a/src/test/java/com/networknt/schema/Issue366FailFastTest.java b/src/test/java/com/networknt/schema/Issue366FailFastTest.java
index 55c94ab..a863ecf 100644
--- a/src/test/java/com/networknt/schema/Issue366FailFastTest.java
+++ b/src/test/java/com/networknt/schema/Issue366FailFastTest.java
@@ -27,7 +27,7 @@ public class Issue366FailFastTest {
         SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
         schemaValidatorsConfig.setFailFast(true);
         JsonSchemaFactory schemaFactory = JsonSchemaFactory
-                .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(objectMapper).build();
+                .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).build();
 
         schemaValidatorsConfig.setTypeLoose(false);
 
diff --git a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
index 7802d4f..38f25a7 100644
--- a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
+++ b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
@@ -26,7 +26,6 @@ public class Issue366FailSlowTest {
     SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
     JsonSchemaFactory schemaFactory = JsonSchemaFactory
         .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
-        .jsonMapper(objectMapper)
         .build();
 
     schemaValidatorsConfig.setTypeLoose(false);
diff --git a/src/test/java/com/networknt/schema/Issue428Test.java b/src/test/java/com/networknt/schema/Issue428Test.java
index c2d3318..5785cec 100644
--- a/src/test/java/com/networknt/schema/Issue428Test.java
+++ b/src/test/java/com/networknt/schema/Issue428Test.java
@@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 public class Issue428Test extends HTTPServiceSupport {
     protected ObjectMapper mapper = new ObjectMapper();
     protected JsonSchemaFactory validatorFactory = JsonSchemaFactory
-            .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).jsonMapper(mapper).build();
+            .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).build();
 
     private void runTestFile(String testCaseFile) throws Exception {
         final SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + testCaseFile);
diff --git a/src/test/java/com/networknt/schema/Issue510Test.java b/src/test/java/com/networknt/schema/Issue510Test.java
deleted file mode 100644
index 2e2da1c..0000000
--- a/src/test/java/com/networknt/schema/Issue510Test.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.networknt.schema;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.jupiter.api.Test;
-
-public class Issue510Test {
-    @Test
-    public void testIssue510() {
-        ObjectMapper objectMapper = new ObjectMapper();
-        JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).jsonMapper(objectMapper).build();
-        System.out.println("schemaFactory = " + schemaFactory);
-    }
-}
diff --git a/src/test/java/com/networknt/schema/Issue928Test.java b/src/test/java/com/networknt/schema/Issue928Test.java
index df933da..96739ae 100644
--- a/src/test/java/com/networknt/schema/Issue928Test.java
+++ b/src/test/java/com/networknt/schema/Issue928Test.java
@@ -10,7 +10,6 @@ public class Issue928Test {
     private JsonSchemaFactory factoryFor(SpecVersion.VersionFlag version) {
         return JsonSchemaFactory
                 .builder(JsonSchemaFactory.getInstance(version))
-                .jsonMapper(mapper)
                 .schemaMappers(schemaMappers -> schemaMappers.mapPrefix("https://example.org", "classpath:"))
                 .build();
     }
diff --git a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
index 157efc3..136f637 100644
--- a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
@@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 public class OpenAPI30JsonSchemaTest extends HTTPServiceSupport {
     protected ObjectMapper mapper = new ObjectMapper();
     protected JsonSchemaFactory validatorFactory = JsonSchemaFactory
-            .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).jsonMapper(mapper).build();
+            .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).build();
 
     public OpenAPI30JsonSchemaTest() {
     }
diff --git a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
index 06c35e4..ed46031 100644
--- a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
+++ b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
@@ -21,7 +21,7 @@ public class UnknownMetaSchemaTest {
         ObjectMapper mapper = new ObjectMapper();
         JsonNode jsonNode = mapper.readTree(this.json);
 
-        JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(mapper).build();
+        JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).build();
         JsonSchema jsonSchema = factory.getSchema(schema1);
 
         Set<ValidationMessage> errors = jsonSchema.validate(jsonNode);
@@ -35,7 +35,7 @@ public class UnknownMetaSchemaTest {
         ObjectMapper mapper = new ObjectMapper();
         JsonNode jsonNode = mapper.readTree(this.json);
 
-        JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(mapper).build();
+        JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).build();
         JsonSchema jsonSchema = factory.getSchema(schema2);
 
         Set<ValidationMessage> errors = jsonSchema.validate(jsonNode);
@@ -48,7 +48,7 @@ public class UnknownMetaSchemaTest {
         ObjectMapper mapper = new ObjectMapper();
         JsonNode jsonNode = mapper.readTree(this.json);
 
-        JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(mapper).build();
+        JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).build();
         JsonSchema jsonSchema = factory.getSchema(schema3);
 
         Set<ValidationMessage> errors = jsonSchema.validate(jsonNode);
diff --git a/src/test/java/com/networknt/schema/V4JsonSchemaTest.java b/src/test/java/com/networknt/schema/V4JsonSchemaTest.java
index 1fc0102..9a71a7a 100644
--- a/src/test/java/com/networknt/schema/V4JsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/V4JsonSchemaTest.java
@@ -89,7 +89,6 @@ public class V4JsonSchemaTest extends HTTPServiceSupport {
         config.setFailFast(true);
         return JsonSchemaFactory
             .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
-            .jsonMapper(objectMapper)
             .build()
             .getSchema(schema, config)
             .validate(dataFile);
diff --git a/src/test/java/com/networknt/schema/serialization/DefaultJsonNodeReaderTest.java b/src/test/java/com/networknt/schema/serialization/DefaultJsonNodeReaderTest.java
new file mode 100644
index 0000000..addf992
--- /dev/null
+++ b/src/test/java/com/networknt/schema/serialization/DefaultJsonNodeReaderTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * 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.
+ */
+package com.networknt.schema.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.utils.JsonNodes;
+
+/**
+ * Test for Default Object Reader.
+ */
+class DefaultJsonNodeReaderTest {
+    @Test
+    void location() throws JsonParseException, IOException {
+        String schemaData = "{\r\n"
+                + "  \"$id\": \"https://schema/myschema\",\r\n"
+                + "  \"properties\": {\r\n"
+                + "    \"startDate\": {\r\n"
+                + "      \"format\": \"date\",\r\n"
+                + "      \"minLength\": 6\r\n"
+                + "    }\r\n"
+                + "  }\r\n"
+                + "}";
+        JsonNode jsonNode = JsonNodeReader.builder().locationAware().build().readTree(schemaData, InputFormat.JSON);
+        JsonNode idNode = jsonNode.at("/$id");
+        JsonLocation location = JsonNodes.tokenLocationOf(idNode);
+        assertEquals(2, location.getLineNr());
+        assertEquals(10, location.getColumnNr());
+
+        JsonNode formatNode = jsonNode.at("/properties/startDate/format");
+        location = JsonNodes.tokenLocationOf(formatNode);
+        assertEquals(5, location.getLineNr());
+        assertEquals(17, location.getColumnNr());
+
+        JsonNode minLengthNode = jsonNode.at("/properties/startDate/minLength");
+        location = JsonNodes.tokenLocationOf(minLengthNode);
+        assertEquals(6, location.getLineNr());
+        assertEquals(20, location.getColumnNr());
+    }
+
+    @Test
+    void jsonLocation() throws IOException {
+        String schemaData = "{\r\n"
+                + "  \"$id\": \"https://schema/myschema\",\r\n"
+                + "  \"properties\": {\r\n"
+                + "    \"startDate\": {\r\n"
+                + "      \"format\": \"date\",\r\n"
+                + "      \"minLength\": 6\r\n"
+                + "    }\r\n"
+                + "  }\r\n"
+                + "}";
+        JsonNode jsonNode = JsonNodeReader.builder().locationAware().build().readTree(schemaData, InputFormat.JSON);
+
+        JsonLocation formatSchemaNodeTokenLocation = JsonNodes.tokenLocationOf(jsonNode.at("/properties/startDate/format"));
+        JsonLocation minLengthSchemaNodeTokenLocation = JsonNodes.tokenLocationOf(jsonNode.at("/properties/startDate/minLength"));
+
+        assertEquals(5, formatSchemaNodeTokenLocation.getLineNr());
+        assertEquals(17, formatSchemaNodeTokenLocation.getColumnNr());
+
+        assertEquals(6, minLengthSchemaNodeTokenLocation.getLineNr());
+        assertEquals(20, minLengthSchemaNodeTokenLocation.getColumnNr());
+    }
+
+    @Test
+    void yamlLocation() throws IOException {
+        String schemaData = "---\r\n"
+                + "\"$id\": 'https://schema/myschema'\r\n"
+                + "properties:\r\n"
+                + "  startDate:\r\n"
+                + "    format: 'date'\r\n"
+                + "    minLength: 6\r\n"
+                + "";
+        JsonNode jsonNode = JsonNodeReader.builder().locationAware().build().readTree(schemaData, InputFormat.YAML);
+
+        JsonLocation formatSchemaNodeTokenLocation = JsonNodes.tokenLocationOf(jsonNode.at("/properties/startDate/format"));
+        JsonLocation minLengthSchemaNodeTokenLocation = JsonNodes.tokenLocationOf(jsonNode.at("/properties/startDate/minLength"));
+
+        assertEquals(5, formatSchemaNodeTokenLocation.getLineNr());
+        assertEquals(13, formatSchemaNodeTokenLocation.getColumnNr());
+
+        assertEquals(6, minLengthSchemaNodeTokenLocation.getLineNr());
+        assertEquals(16, minLengthSchemaNodeTokenLocation.getColumnNr());
+    }
+}
diff --git a/src/test/java/com/networknt/schema/utils/JsonNodesTest.java b/src/test/java/com/networknt/schema/utils/JsonNodesTest.java
index 71a2abb..24c55c7 100644
--- a/src/test/java/com/networknt/schema/utils/JsonNodesTest.java
+++ b/src/test/java/com/networknt/schema/utils/JsonNodesTest.java
@@ -40,6 +40,7 @@ import com.networknt.schema.SchemaValidatorsConfig;
 import com.networknt.schema.SpecVersion.VersionFlag;
 import com.networknt.schema.ValidationMessage;
 import com.networknt.schema.serialization.JsonMapperFactory;
+import com.networknt.schema.serialization.JsonNodeReader;
 import com.networknt.schema.serialization.node.LocationJsonNodeFactoryFactory;
 /**
  * Tests for JsonNodes.
@@ -73,7 +74,7 @@ public class JsonNodesTest {
         assertEquals(6, location.getLineNr());
         assertEquals(20, location.getColumnNr());
     }
-    
+
     @Test
     void jsonLocation() {
         String schemaData = "{\r\n"
@@ -89,7 +90,7 @@ public class JsonNodesTest {
                 + "  \"startDate\": \"1\"\r\n"
                 + "}";
         JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012,
-                builder -> builder.jsonNodeFactoryFactory(LocationJsonNodeFactoryFactory.getInstance()));
+                builder -> builder.jsonNodeReader(JsonNodeReader.builder().locationAware().build()));
         SchemaValidatorsConfig config = new SchemaValidatorsConfig();
         config.setPathType(PathType.JSON_POINTER);
         JsonSchema schema = factory.getSchema(schemaData, InputFormat.JSON, config);
@@ -138,7 +139,7 @@ public class JsonNodesTest {
                 + "startDate: '1'\r\n"
                 + "";
         JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012,
-                builder -> builder.jsonNodeFactoryFactory(LocationJsonNodeFactoryFactory.getInstance()));
+                builder -> builder.jsonNodeReader(JsonNodeReader.builder().locationAware().build()));
         SchemaValidatorsConfig config = new SchemaValidatorsConfig();
         config.setPathType(PathType.JSON_POINTER);
         JsonSchema schema = factory.getSchema(schemaData, InputFormat.YAML, config);