diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ee12093
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/target
+.idea
+JsonSchema.iml
diff --git a/example/example1/objectArrayInObject.json b/example/example1/objectArrayInObject.json
new file mode 100644
index 0000000..c4ff533
--- /dev/null
+++ b/example/example1/objectArrayInObject.json
@@ -0,0 +1,8 @@
+{
+ "name": "john",
+ "age": 30,
+ "hobbies": [
+ { "name" : "soccer", "howFreq": 10 },
+ { "name" : "swimming", "howFreq": 1}
+ ]
+}
\ No newline at end of file
diff --git a/example/example1/objectArrayInObject_schema1.json b/example/example1/objectArrayInObject_schema1.json
new file mode 100644
index 0000000..06d07ba
--- /dev/null
+++ b/example/example1/objectArrayInObject_schema1.json
@@ -0,0 +1,22 @@
+[
+ {
+ "type": "array",
+ "namespace": "hobby",
+ "name": "arr",
+ "doc": "simple sub array",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "howFreq", "type": "Integer"}
+ ]
+ },
+ {
+ "type": "object",
+ "namespace": "simple",
+ "name": "test",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "age", "type": "Integer"},
+ {"name": "hobbies", "type": "hobby.arr"}
+ ]
+ }
+]
\ No newline at end of file
diff --git a/example/example1/objectArrayInObject_schema2.json b/example/example1/objectArrayInObject_schema2.json
new file mode 100644
index 0000000..b9fc985
--- /dev/null
+++ b/example/example1/objectArrayInObject_schema2.json
@@ -0,0 +1,22 @@
+[
+ {
+ "type": "object",
+ "namespace": "hobby",
+ "name": "obj",
+ "doc": "simple",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "howFreq", "type": "Integer"}
+ ]
+ },
+ {
+ "type": "object",
+ "namespace": "simple",
+ "name": "test",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "age", "type": "Integer"},
+ {"name": "hobbies", "type": "hobby.obj[]"}
+ ]
+ }
+]
\ No newline at end of file
diff --git a/example/example2/arraysInsideObject.json b/example/example2/arraysInsideObject.json
new file mode 100644
index 0000000..8ba29a9
--- /dev/null
+++ b/example/example2/arraysInsideObject.json
@@ -0,0 +1,4 @@
+{
+ "subject": ["math", "english", "french"],
+ "score": [20, 24, 30]
+}
\ No newline at end of file
diff --git a/example/example2/arraysInsideObject_schema.json b/example/example2/arraysInsideObject_schema.json
new file mode 100644
index 0000000..55d5a50
--- /dev/null
+++ b/example/example2/arraysInsideObject_schema.json
@@ -0,0 +1,12 @@
+[
+ {
+ "type": "object",
+ "namespace": "simple",
+ "name": "test2",
+ "doc": "simple",
+ "fields": [
+ {"name": "subject", "type": "String[]"},
+ {"name": "score", "type": "Integer[]"}
+ ]
+ }
+]
\ No newline at end of file
diff --git a/example/example3/simpleObjectInArray.json b/example/example3/simpleObjectInArray.json
new file mode 100644
index 0000000..5907aa5
--- /dev/null
+++ b/example/example3/simpleObjectInArray.json
@@ -0,0 +1,5 @@
+[
+ {"subject": "math", "score": 80.2 },
+ {"subject": "english", "score": 10.2 },
+ {"subject": "french", "score": 20.2 }
+]
\ No newline at end of file
diff --git a/example/example3/simpleObjectInArray_schema.json b/example/example3/simpleObjectInArray_schema.json
new file mode 100644
index 0000000..1d92246
--- /dev/null
+++ b/example/example3/simpleObjectInArray_schema.json
@@ -0,0 +1,12 @@
+[
+ {
+ "type": "array",
+ "namespace": "simple",
+ "name": "test3",
+ "doc": "simple",
+ "fields": [
+ {"name": "subject", "type": "String"},
+ {"name": "score", "type": "Double"}
+ ]
+ }
+]
\ No newline at end of file
diff --git a/example/example4/usingRegex.json b/example/example4/usingRegex.json
new file mode 100644
index 0000000..6384d12
--- /dev/null
+++ b/example/example4/usingRegex.json
@@ -0,0 +1,4 @@
+{
+ "/applicant-details/applicant-1/address-history" : "init",
+ "/applicant-details/applicant-2/address-history" : "deleted"
+}
\ No newline at end of file
diff --git a/example/example4/usingRegex_schema.json b/example/example4/usingRegex_schema.json
new file mode 100644
index 0000000..6af567e
--- /dev/null
+++ b/example/example4/usingRegex_schema.json
@@ -0,0 +1,13 @@
+[
+ {
+ "type": "object",
+ "namespace": "simple",
+ "name": "test4",
+ "doc": "validation schema",
+ "fields" : [
+ { "name": "$REGEX$\\/applicant-details\\/applicant-([0-9])+\\/address-history", "type": "String" }
+ ]
+ }
+]
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..83c1499
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+ com.github.sijoonlee
+ JsonSchemaValidator
+ 1.0-SNAPSHOT
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.5
+
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.30
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.7.0
+
+
+ 1.8
+
+
+
+
+
\ No newline at end of file
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..04376d5
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,185 @@
+## Simple JSON Schema and its Validator
+- How I structured JSON schema is affected by [Apache Avro Schema](https://avro.apache.org/)
+ - Not exactly same, though
+- There is an existing draft of [JSON schema](https://tools.ietf.org/html/draft-zyp-json-schema-04)
+ - But, in my opinion, this is far from easy-to-use
+
+## if you want to see examples first
+- Please see **example** folder and **test** folder
+
+## Terminology
+- There are two basic structures of Json
+ - One is **object**, which is wrapped by { }
+ - The other is **array**, which is wrapped by [ ]
+- Json object can have zero or more **fields**
+- Each field has **name** and **value**
+- Below example has one field, of which "name" is "person" and "value" is "john"
+ ```
+ {
+ "person": "john"
+ }
+ ```
+
+### How to write Schema
+- Schema is written as Json of which root element should be 'array'
+- It is because Schema Json file can contain multiple set of Schema
+- Each Schema set is called **Schema Record**
+
+ ```
+ [ // root is always array
+ { }, // one schema set, which I call 'record'
+ { } // second schema record
+ ]
+ ```
+
+- The schema record has fields: 'type', 'namespace', 'name', 'fields'
+ ```
+ [
+ {
+ "type" : "object"
+ "namespace" : "schema.omp"
+ "name": "lead"
+ "fields" : [
+ { "name": "age", "type": "Integer" },
+ { "name": "job", "type": "String" }
+ ]
+ },
+ {
+ // another schema record
+ }
+
+ ]
+ ```
+ - 'type' : it is either 'object' or 'array'
+ - 'namespace' : it is to avoid naming conflicts (ex) schema.omp
+ - 'name' : name of the schema (ex) lead
+ - note that full name of schema record is referred as [namespace].[name]
+ - full name example: schema.omp.lead
+ - 'fields' contains the information of fields inside Json obejct or Json array
+ - each field consists of field "name" and field "type"
+ - for example, { "name" : "age", "type" : "Integer" }
+ - 7 types exist for type-checking
+ - 6 types coming from Java data types
+ - String, Integer, BigInteger, Double, Boolean, OffsetDateTime
+ - Type-checking is simply to ensure the value can be parsed with that type
+ - for example, if value is "10.2", it can't be pared as Integer
+ - 1 more type for passing around the type-checking process
+ - TypeCheckingPass
+ - You can define customized types (see below section)
+ - A customized type is a schema record
+ - To use customized type, use full name of it as "type"
+ - for example, if schema record's full name is schema.person
+ ```
+ { "name" : "person", "type" : "schema.person" }
+ ```
+### Json Example
+- This example's root structure is "object" not "array" since the outer-most wrapping is done by { and }
+- This example contains 3 fields: name, age, hobbies
+- The fields 'hobbies' has an array of objects as its value
+ ```
+ {
+ "name": "john",
+ "age": 30,
+ "hobbies": [
+ { "name" : "soccer", "howFreq": 10 },
+ { "name" : "swimming", "howFreq": 1}
+ ]
+ }
+ ```
+
+### Writing Schema for the Example Json - 1
+
+```
+[
+ {
+ "type": "object", // this is referring to the root structure, which always is either 'object' or 'array'
+ "namespace": "simple",
+ "name": "object",
+ "fields": [
+ {"name": "name", "type": "String"}, // There are 6 types supported: String, Integer, BigInteger, Double, Boolean, OffsetDateTime
+ {"name": "age", "type": "Integer"},
+ {"name": "hobbies", "type": "hobby.array"} // This is Customized type, "hobby.array" refers to the full name of the other schema record
+ ]
+ },
+ {
+ "type": "array", // this is referring to the array of objects (hobbies field)
+ "namespace": "hobby",
+ "name": "array", // full name is "hobby.array"
+ "doc": "simple sub array",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "howFreq", "type": "Integer"}
+ ]
+ }
+]
+```
+
+### Writing Schema for the Example Json - 2
+- This does the same thing as the previous example
+```
+[
+ {
+ "type": "object",
+ "namespace": "simple",
+ "name": "test",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "age", "type": "Integer"},
+ {"name": "hobbies", "type": "hobby.object[]"} // It is possible to make an array from an object by attaching [] at the end of the full name
+ ]
+ },
+ {
+ "type": "object", // note that it is 'object'
+ "namespace": "hobby",
+ "name": "object", // full name is 'hobby.object'
+ "doc": "simple",
+ "fields": [
+ {"name": "name", "type": "String"},
+ {"name": "howFreq", "type": "Integer"}
+ ]
+ }
+]
+```
+
+- please note that it's possible to use String[], Integer[] and so on
+
+## You can use Regex pattern for field name
+- use prefix "$REGEX$"
+- use escape characters where they are needed
+- example
+ ```
+ [
+ {
+ "type": "object",
+ "namespace": "simple",
+ "name": "test",
+ "fields": [
+ {"name": "$REGEX$\\/applicant-details\\/applicant-([0-9])+\\/address-history", "type": "String"}
+ ]
+ }
+ }
+ ```
+ - if the field name is "/applicant-details/applicant-2/address-history", it is valid
+
+## How to use Schema Classes
+1. Instantiate validator with schema json file
+- example
+ ```
+ SchemaValidator schemaValidator = new SchemaValidator("testData/schema/salesforceSchema.json");
+ ```
+2. Run validator with JsonElement (gson class)
+ ```
+ JsonElement json = [some process]
+ boolean isValid = schemaValidator.runWithJsonElement(json, "simple.test"); // the second argument 'simple.test' is the full name of the schema record of root element
+ ```
+3. or Run validator with Json file
+ ```
+ boolean isValid = schemaValidator.runWithJsonFile("example/simpleStructure.json", "simple.test");
+
+ ```
+
+## TODO
+- There is no Regex checking for 'value'
+- There is no required field checking
+ - Currently, error occurs only when it finds a field not listed in schema
+ - Therefore, it doesn't make an error when some fields in schema are missing
\ No newline at end of file
diff --git a/src/main/java/com/github/sijoonlee/SchemaRecord.java b/src/main/java/com/github/sijoonlee/SchemaRecord.java
new file mode 100644
index 0000000..d81bb6d
--- /dev/null
+++ b/src/main/java/com/github/sijoonlee/SchemaRecord.java
@@ -0,0 +1,117 @@
+package com.github.sijoonlee;
+
+import java.util.*;
+
+/* Gson will populate data into this class from Schema json file
+ * Please refer SchemaValiadator's constructor
+ */
+public class SchemaRecord {
+
+ private String type;
+ private String namespace;
+ private String name;
+ private String doc;
+ private ArrayList fields;
+
+ class SchemaField {
+ private String name;
+ private String type;
+ private String doc;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDoc() {
+ return doc;
+ }
+
+ public void setDoc(String doc) {
+ this.doc = doc;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return namespace + "." + name + ": " + doc;
+ }
+
+ public String getName(){
+ return name;
+ }
+ public String getNamespace(){
+ return namespace;
+ }
+ public String getFullNamePath(){
+ return namespace + "." + name;
+ }
+ public String getDoc() {
+ return doc;
+ }
+ public ArrayList getFields() {
+ return fields;
+ }
+
+ public Set getFieldNames() {
+ Set names = new HashSet<>();
+ for(SchemaField field: fields) {
+ names.add(field.name);
+ }
+ return names;
+ }
+
+ public String getType(){
+ return type;
+ }
+
+ public String getFieldType(String name) {
+ String type = "";
+ for(SchemaField field: fields) {
+ if( name.equals(field.name) ) {
+ type = field.type;
+ break;
+ }
+ }
+ return type;
+ }
+
+ public Map getFieldProperties() {
+ Map entries = new HashMap<>();
+ for(SchemaField field: fields){
+ entries.put(field.name, field.type);
+ }
+ return entries;
+ }
+
+ public String findFieldTypeFromFieldName(String name) {
+ final String REGEX_PREFIX = "$REGEX$";
+ String type = null;
+ String pattern = "";
+ for(SchemaField field: fields){
+ if(field.name.equals(name)){
+ type = field.type;
+ break;
+ } else if(field.name.startsWith(REGEX_PREFIX)){
+ pattern = field.name.substring(REGEX_PREFIX.length());
+ if(name.matches(pattern)){
+ type = field.type;
+ break;
+ }
+ }
+ }
+ return type;
+ }
+
+}
diff --git a/src/main/java/com/github/sijoonlee/SchemaValidator.java b/src/main/java/com/github/sijoonlee/SchemaValidator.java
new file mode 100644
index 0000000..85eaf46
--- /dev/null
+++ b/src/main/java/com/github/sijoonlee/SchemaValidator.java
@@ -0,0 +1,299 @@
+package com.github.sijoonlee;
+
+import com.github.sijoonlee.util.JsonUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.github.sijoonlee.util.JsonUtil;
+
+import java.math.BigInteger;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.Map.Entry;
+
+public class SchemaValidator {
+ private final ArrayList schemas;
+ private final ArrayList recordNamedTypes;
+ private final ArrayList recordNamedArrayTypes;
+ private final ArrayList javaTypes;
+ private final ArrayList javaArrayTypes;
+ private static final Logger log = LoggerFactory.getLogger(SchemaValidator.class.getName());
+ private final String FIELD_NAME_NOT_EXIST = "FIELD_NAME_NOT_EXIST";
+ private final String TYPE_STRING = "String";
+ private final String TYPE_INTEGER = "Integer";
+ private final String TYPE_DOUBLE = "Double";
+ private final String TYPE_BIGINTEGER = "BigInteger";
+ private final String TYPE_BOOLEAN = "Boolean";
+ private final String TYPE_OFFSETDATETIME = "OffsetDateTime";
+ private final String TYPE_CHECKING_PASS = "TypeCheckingPass";
+
+ /*
+ @FirstParam: the path of Schema json file
+ */
+ public SchemaValidator(String schemaPath) {
+
+ // import schemas
+ schemas = new ArrayList<>();
+ JsonArray jsonArray = JsonUtil.convertJsonFileToJsonArray(schemaPath); // this is because Schema's root element is always Json array
+ Gson gson = new Gson();
+ for (JsonElement elm : jsonArray) {
+ // convert each item in array into SchemaRecord instance and push it to ArrayList
+ schemas.add(gson.fromJson(elm, SchemaRecord.class));
+ }
+
+ // Collect schema record's full names (ex: schema.omp.lead )
+ // these full names can be referred as customized types
+ recordNamedTypes = getSchemaRecordNames(schemas);
+
+ // init supported Java types
+ javaTypes = new ArrayList<>();
+ javaTypes.add(TYPE_STRING);
+ javaTypes.add(TYPE_INTEGER);
+ javaTypes.add(TYPE_BIGINTEGER);
+ javaTypes.add(TYPE_DOUBLE);
+ javaTypes.add(TYPE_BOOLEAN);
+ javaTypes.add(TYPE_OFFSETDATETIME);
+
+ // init array types derived from Java types (ex: String[] )
+ javaArrayTypes = new ArrayList<>();
+ for(String type : javaTypes){
+ javaArrayTypes.add(type + "[]");
+ }
+
+ // init array types derived from customized types (ex: schema.omp.lead[] )
+ recordNamedArrayTypes = new ArrayList<>();
+ for(String type: recordNamedTypes){
+ recordNamedArrayTypes.add(type + "[]");
+ }
+
+ }
+
+
+ private void parsingTest(String type, String value) throws Exception {
+ if (type.equals(TYPE_INTEGER)) {
+ try {
+ Integer.parseInt(value);
+ } catch (Exception ex) {
+ throw new Exception( "Field Type Error: can't be Integer - " + value);
+ }
+ } else if (type.equals(TYPE_BIGINTEGER)) {
+ try {
+ Long longValue = Long.parseLong(value);
+ BigInteger.valueOf(longValue);
+ } catch (Exception ex) {
+ throw new Exception( "Field Type Error: can't be BigInteger - " + value);
+ }
+ } else if (type.equals(TYPE_DOUBLE)) {
+ try {
+ Double.parseDouble(value);
+ } catch (Exception ex) {
+ throw new Exception( "Field Type Error: can't be Double - " + value);
+ }
+ } else if (type.equals(TYPE_BOOLEAN)) {
+ try {
+ Boolean.parseBoolean(value);
+ } catch (Exception ex) {
+ throw new Exception( "Field Type Error: can't be Boolean - " + value);
+ }
+ } else if (type.equals(TYPE_OFFSETDATETIME)) {
+ try {
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+ OffsetDateTime.parse(value, dateTimeFormatter);
+ } catch (Exception ex) {
+ throw new Exception( "Field Type Error: can't be OffsetDateTime - " + value);
+ }
+ }
+
+ }
+
+ private ArrayList getSchemaRecordNames(ArrayList schemas) {
+ ArrayList names = new ArrayList<>();
+ for (SchemaRecord schema : schemas) {
+ names.add(schema.getFullNamePath());
+ }
+ return names;
+ }
+ private class FieldInfo {
+ public String type ;
+ public String name;
+ public JsonElement value;
+
+ public FieldInfo(String type, String name, JsonElement value) {
+ this.type = type;
+ this.name = name;
+ this.value = value;
+ }
+ }
+
+ private String getFieldType(int indexOfSchema, String name) throws Exception {
+ String type;
+ type = schemas.get(indexOfSchema).findFieldTypeFromFieldName(name);
+ if(type == null) {
+ throw new Exception("Field Name Error: can't find the field name in schema - " + name);
+ }
+ return type;
+ }
+
+ private void putFieldIntoStack(Deque stack, Set> entries, int indexOfSchema) {
+ for (Entry entry : entries){
+ String type = FIELD_NAME_NOT_EXIST;
+ try{
+ type = getFieldType(indexOfSchema, entry.getKey());
+ } catch (Exception ex) {
+ log.error("putFieldIntoStack");
+ log.error(ex.getMessage());
+ }
+ stack.push(new FieldInfo(type, entry.getKey(), entry.getValue()));
+ }
+ }
+
+ /*
+ @FirstParam: it is the path of json file to be validated
+ @SecondParam: it is the full name of the schema record, which is specified inside of Schema json file
+ */
+ public boolean runWithJsonFile(String targetJsonFilePath, String mainSchemaName){
+ JsonElement targetJson;
+ int indexOfSchema = recordNamedTypes.indexOf(mainSchemaName);
+ if(indexOfSchema < 0) {
+ log.error("Main schema record's full name was wrong");
+ return false;
+ }
+ if(schemas.get(indexOfSchema).getType().equals("array")){
+ targetJson = JsonUtil.convertJsonFileToJsonArray(targetJsonFilePath);
+ } else if (schemas.get(indexOfSchema).getType().equals("object")) {
+ targetJson = JsonUtil.convertJsonFileToJsonObject(targetJsonFilePath);
+ } else {
+ log.error("record type should be 'array' or 'object'");
+ return false;
+ }
+ return runWithJsonElement(targetJson, mainSchemaName);
+ }
+
+ /*
+ @FirstParam: it is Gson's JsonElement object to be validated
+ @SecondParam: it is the full name of the schema record, which is specified inside of Schema json file
+ */
+ public boolean runWithJsonElement(JsonElement targetJsonElement, String mainSchemaName) {
+ boolean isValid = true;
+ int fieldCounter = 0;
+
+ // prepare
+ int indexOfSchema = recordNamedTypes.indexOf(mainSchemaName);
+ if(indexOfSchema < 0) {
+ log.error("Main schema name was wrong");
+ return false;
+ }
+ Deque stack = new ArrayDeque<>();
+ Set> entries; //
+ FieldInfo field;
+
+ // Generating Initial Stack
+ // - check if it is json object or json array
+ // - if is array, unfold array and put all items into stack
+ // - if is object, put all items into stack
+ if(schemas.get(indexOfSchema).getType().equals("array")) {
+
+ JsonArray targetJson = targetJsonElement.getAsJsonArray();
+ for(JsonElement arrayItem : targetJson){
+ entries = arrayItem.getAsJsonObject().entrySet();
+ putFieldIntoStack(stack, entries, indexOfSchema);
+ }
+ } else if (schemas.get(indexOfSchema).getType().equals("object")) {
+
+ JsonObject targetJson = targetJsonElement.getAsJsonObject();
+ entries = targetJson.getAsJsonObject().entrySet();
+ putFieldIntoStack(stack, entries, indexOfSchema);
+ } else {
+ log.error("record type should be 'array' or 'object'");
+ return false;
+ }
+
+ // pop a field from stack and validate it
+ while (stack.size() > 0) {
+ field = stack.pop();
+ if (javaTypes.contains(field.type)) {
+ // System.out.println(field.name);
+ fieldCounter += 1;
+ try {
+ parsingTest(field.type, field.value.getAsString());
+ } catch (Exception ex){
+ isValid = false;
+ log.error(ex.getMessage() + " - " + field.name);
+ };
+
+ } else if (javaArrayTypes.contains(field.type)) {
+ // System.out.println(field.name);
+ fieldCounter += 1;
+ for(JsonElement arrayItem :field.value.getAsJsonArray()){
+ try{
+ // field.type looks like "String[]"
+ // field.type.substring(0, field.type.length()-2) is to delete "[]"
+ parsingTest(field.type.substring(0, field.type.length()-2), arrayItem.getAsString());
+ } catch (Exception ex){
+ isValid = false;
+ log.error(ex.getMessage() + " - " + field.name);
+ }
+ }
+
+ } else if (recordNamedTypes.contains(field.type)) {
+ // System.out.println(field.name);
+ fieldCounter += 1;
+ indexOfSchema = recordNamedTypes.indexOf(field.type);
+ if(schemas.get(indexOfSchema).getType().equals("array")){
+ JsonArray targetJson = field.value.getAsJsonArray();
+ for(JsonElement arrayItem : targetJson){
+ entries = arrayItem.getAsJsonObject().entrySet();
+ putFieldIntoStack(stack, entries, indexOfSchema);
+ }
+ } else if (schemas.get(indexOfSchema).getType().equals("object")){
+ entries = field.value.getAsJsonObject().entrySet();
+ putFieldIntoStack(stack, entries, indexOfSchema);
+ } else {
+ log.error("record type should be 'array' or 'object'");
+ return false;
+ }
+ } else if (recordNamedArrayTypes.contains(field.type)) {
+ // System.out.println(field.name);
+ fieldCounter += 1;
+ indexOfSchema = recordNamedTypes.indexOf(field.type.substring(0, field.type.length()-2));
+ JsonArray targetJson = field.value.getAsJsonArray();
+ for(JsonElement arrayItem : targetJson) {
+ if(schemas.get(indexOfSchema).getType().equals("array")){
+ JsonArray nestedArray = arrayItem.getAsJsonArray();
+ for(JsonElement nestedArrayItem : nestedArray){
+ entries = nestedArrayItem.getAsJsonObject().entrySet();
+ putFieldIntoStack(stack, entries, indexOfSchema);
+ }
+ } else if (schemas.get(indexOfSchema).getType().equals("object")){
+ entries = arrayItem.getAsJsonObject().entrySet();
+ putFieldIntoStack(stack, entries, indexOfSchema);
+ } else {
+ log.error("record type should be 'array' or 'object'");
+ return false;
+ }
+ }
+
+ } else if (field.type.equals(TYPE_CHECKING_PASS)) {
+ // do nothing
+ fieldCounter += 1;
+ log.info("Type checking passed : " + field.name);
+ } else if (field.type.equals(FIELD_NAME_NOT_EXIST)) {
+ fieldCounter += 1;
+ isValid = false;
+ } else {
+ fieldCounter += 1;
+ isValid = false;
+ log.error("Unrecognized type provided: " + field.name + " - " + field.type);
+ }
+
+ }
+ log.info("Field Counts: " + Integer.toString(fieldCounter));
+ return isValid;
+ }
+
+}
diff --git a/src/main/java/com/github/sijoonlee/util/JsonUtil.java b/src/main/java/com/github/sijoonlee/util/JsonUtil.java
new file mode 100644
index 0000000..18ca7ba
--- /dev/null
+++ b/src/main/java/com/github/sijoonlee/util/JsonUtil.java
@@ -0,0 +1,60 @@
+package com.github.sijoonlee.util;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+
+public class JsonUtil {
+ static public Charset encoding = StandardCharsets.UTF_8;
+
+ static public JsonObject ConvertObjToJsonObj(Object src) {
+ Gson gson = new Gson();
+ JsonElement jsonElement = gson.toJsonTree(src);
+ return (JsonObject) jsonElement;
+ }
+
+ public static JsonObject convertJsonFileToJsonObject(String filePath) {
+ JsonObject jsonObject = null;
+ try {
+ Gson gson = new Gson();
+ jsonObject = gson.fromJson(new FileReader(filePath), JsonObject.class);
+ } catch (FileNotFoundException ex){
+ System.out.println(ex.toString());
+ }
+ return jsonObject;
+ }
+
+ public static JsonArray convertJsonFileToJsonArray(String filePath) {
+ JsonArray jsonArray = null;
+ try {
+ Gson gson = new Gson();
+ jsonArray = gson.fromJson(new FileReader(filePath), JsonArray.class);
+ } catch (FileNotFoundException ex){
+ System.out.println(ex.toString());
+ }
+
+ return jsonArray;
+ }
+
+
+ static public String readJsonFileToString(String path) {
+ try{
+ byte[] encoded = Files.readAllBytes(Paths.get(path));
+ return new String(encoded, encoding);
+ } catch (IOException ex) {
+ System.out.println(ex.toString());
+ return null;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/TestValidator.java b/src/test/java/TestValidator.java
new file mode 100644
index 0000000..101e7d3
--- /dev/null
+++ b/src/test/java/TestValidator.java
@@ -0,0 +1,46 @@
+import com.github.sijoonlee.SchemaValidator;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+public class TestValidator {
+
+ @Test
+ public void example1() {
+
+ boolean isValid;
+
+ SchemaValidator validator1 = new SchemaValidator("example/example1/objectArrayInObject_schema1.json");
+
+ isValid = validator1.runWithJsonFile("example/example1/objectArrayInObject.json", "simple.test");
+ assertTrue(isValid);
+
+ SchemaValidator validator2 = new SchemaValidator("example/example1/objectArrayInObject_schema2.json");
+ isValid = validator2.runWithJsonFile("example/example1/objectArrayInObject.json", "simple.test");
+ assertTrue(isValid);
+ }
+
+ @Test
+ public void example2() {
+ boolean isValid;
+ SchemaValidator validator = new SchemaValidator("example/example2/arraysInsideObject_schema.json");
+ isValid = validator.runWithJsonFile("example/example2/arraysInsideObject.json", "simple.test2");
+ assertTrue(isValid);
+ }
+
+ @Test
+ public void example3() {
+ boolean isValid;
+ SchemaValidator validator = new SchemaValidator("example/example3/simpleObjectInArray_schema.json");
+ isValid = validator.runWithJsonFile("example/example3/simpleObjectInArray.json", "simple.test3");
+ assertTrue(isValid);
+ }
+
+ @Test
+ public void example4() {
+ boolean isValid;
+ SchemaValidator validator = new SchemaValidator("example/example4/usingRegex_schema.json");
+ isValid = validator.runWithJsonFile("example/example4/usingRegex.json", "simple.test4");
+ assertTrue(isValid);
+ }
+}