madison54
madison54

Reputation: 763

Validate JSON schema compliance with Jackson against an external schema file

I would like to use the Jackson library (https://github.com/FasterXML/jackson) to deal with JSON files in Java, which are described by a JSON schema file.

Now, I would like to validate, if a parsed JSON complies with a JSON schema file, which is parsed by itself.

There is a JSON schema module for Jackson (https://github.com/FasterXML/jackson-module-jsonSchema). However, it appears to me that its primary focus is on creating a JSON schema file from within Java.

What is a good way to validate a JSON schema in Java? - preferably using Jackson, but I am also open to other solutions.

Upvotes: 12

Views: 32947

Answers (4)

suhas0sn07
suhas0sn07

Reputation: 718

As described here, the feture developement of the jackson validator has been stoped.

However, I found networknt's json schema validator much live and interesting as of now. You can refer these contents for quick start.

Maven dependency

<dependency>
   <groupId>com.networknt</groupId>
   <artifactId>json-schema-validator</artifactId>
  <version>1.0.49</version>
</dependency>

Code sneppet -

String jsonOrder = SampleUtil.getSampleJsonOrder();//replace or write
    System.out.println(jsonOrder);
    //Read json schema from classpaht 
    JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
    InputStream is = Thread.currentThread().getContextClassLoader()
            .getResourceAsStream("order-schema.json");
    JsonSchema schema = factory.getSchema(is);
    
    //Read json to validate 
    try {
        JsonNode node = mapper.readTree(jsonOrder);
        
        Set<ValidationMessage> errors = schema.validate(node);
        System.out.println("Errors in first json object: " + errors);
    } catch (IOException e) {
        e.printStackTrace();
    } 
    
    //Test for invalid json
    String emptyFoIdOrder = "{\"gtins\":[\"1\",\"2\",\"3\",\"4\"],\"storeId\":121,\"deliveryAddress\":\"Any street, some house - PIN 2021\"}/";

    
    try {
        JsonNode node = mapper.readTree(emptyFoIdOrder);
        Set<ValidationMessage> errors = schema.validate(node);
        System.out.println("Errors in first json object: " + errors);
    } catch (IOException e) {
        e.printStackTrace();
    } 

Sample Json scheme used-

{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "object",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": {},
"examples": [
    {
        "foId": "9876",
        "gtins": [
            "1",
            "2",
            "3",
            "4"
        ],
        "storeId": 121,
        "deliveryAddress": "Any streeam, some house - PIN 2021"
    }
],
"required": [
    "foId",
    "gtins",
    "storeId",
    "deliveryAddress"
],
"properties": {
    "foId": {
        "$id": "#/properties/foId",
        "type": "string",
        "title": "The foId schema",
        "description": "An explanation about the purpose of this instance.",
        "default": "",
        "examples": [
            "9876"
        ]
    },
    "gtins": {
        "$id": "#/properties/gtins",
        "type": "array",
        "title": "The gtins schema",
        "description": "An explanation about the purpose of this instance.",
        "default": [],
        "examples": [
            [
                "1",
                "2"
            ]
        ],
        "additionalItems": true,
        "items": {
            "$id": "#/properties/gtins/items",
            "anyOf": [
                {
                    "$id": "#/properties/gtins/items/anyOf/0",
                    "type": "string",
                    "title": "The first anyOf schema",
                    "description": "An explanation about the purpose of this instance.",
                    "default": "",
                    "examples": [
                        "1",
                        "2"
                    ]
                }
            ]
        }
    },
    "storeId": {
        "$id": "#/properties/storeId",
        "type": "integer",
        "title": "The storeId schema",
        "description": "An explanation about the purpose of this instance.",
        "default": 0,
        "examples": [
            121
        ]
    },
    "deliveryAddress": {
        "$id": "#/properties/deliveryAddress",
        "type": "string",
        "title": "The deliveryAddress schema",
        "description": "An explanation about the purpose of this instance.",
        "default": "",
        "examples": [
            "Any streeam, some house - PIN 2021"
        ]
    }
},
"additionalProperties": true

}

Upvotes: 3

Matthias Wiedemann
Matthias Wiedemann

Reputation: 1599

Just stumbled about https://github.com/leadpony/justify another implementation of a validator for json schema, also more recent draft versions. (7,6,4)

Upvotes: 2

Ajay Kumar
Ajay Kumar

Reputation: 5233

1.) Add Dependency pom.xml :-

    <dependency>
        <groupId>com.github.fge</groupId>
        <artifactId>json-schema-validator</artifactId>
        <version>2.2.6</version>
    </dependency>

2.) NoSqlEntity is a meta data for entity that can reside in no-sql database.

Initialized NoSqlEntity with schema file.

public static final NoSqlEntity entity = new NoSqlEntity("PAYOUT_ENTITY", "DB_","/schema/payout_entity.json");

public class NoSqlEntity {
private static final Map<String, NoSqlEntity> STORE = new HashMap<>();

private final AtomicLong seq = new AtomicLong(System.currentTimeMillis());
private IdentityGenerator identityGenerator;
private String entity;
private String collectionName;
private String jsonSchema;
private String idColumn = "id";
private String database;


public NoSqlEntity(String entity, String idColumn, String collectionPrefix,      String jsonSchema) {
    this.entity = entity;
    this.idColumn = idColumn;

    this.collectionName = collectionPrefix + "_" + entity;
    this.jsonSchema = jsonSchema;
    STORE.put(entity, this);
}

public NoSqlEntity(String collectionName, String jsonSchema) {
    this.collectionName = collectionName;
    this.jsonSchema = jsonSchema;
}

public static NoSqlEntity valueOf(String entityType) {
    return STORE.get(entityType);
}

public boolean isNotNullSchema() {
    return jsonSchema != null;
}
 ...
 // Other Getter/Setter properties and methods.
}

3.) Sample format of validation schema file of payout_entity.json-

   {
    "properties":{
          "txId":{"type":"string"}

    }
    "required" :["txId","currency"]
  }

4.) JsonSchemaManager - Validate the incoming JSON schema and cache the schema as well.

public class JsonSchemaManager {
private final static Logger LOGGER = LoggerFactory.getLogger(JsonSchemaManager.class);
protected final static String LS = StandardSystemProperty.LINE_SEPARATOR.value();

private final JsonValidator validator = JsonSchemaFactory.byDefault().getValidator();
private final Map<NoSqlEntity, JsonNode> schemaMap = new HashMap<>();

public JsonNode load(NoSqlEntity noSqlEntity) throws IOException {
    final JsonNode schema = JsonLoader.fromURL(this.getClass().getResource(noSqlEntity.getJsonSchema()));
    schemaMap.put(noSqlEntity, schema);
    return schema;
}

public void validateSchema(NoSqlEntity noSqlEntity, JsonNode toBeValidated, Consumer<ProcessingReport> consumer) {
    try {
        JsonNode schema = schemaMap.get(noSqlEntity);
        if (schema == null) {
            schema = load(noSqlEntity);
        }
        final ProcessingReport report = validator.validate(schema, toBeValidated);
        if (!report.isSuccess()) {
            consumer.accept(report);
        }
    } catch (IOException ex) { //NOSONAR
        throw new InvalidRequestException(ex.toString());
    } catch (ProcessingException ex) { //NOSONAR
        throw new InvalidRequestException(ex.toString());
    }
}

 public synchronized boolean synchronizedCheck(NoSqlEntity noSqlEntity, JsonNode toBeValidated, Consumer<Map<String, Object>> messageConsumers) {
    boolean flags = CommonUtils.unchecked(() -> {
        validateSchema(noSqlEntity, toBeValidated, report -> {
            report.forEach(processingMessage -> messageConsumers.accept(JsonConverter.jsonAsMapObject(processingMessage.asJson())));
        });
        return true;
    }, ex -> {
        throw new RuntimeException(ex.toString()); //NOSONAR
    });
    return flags;
}
}

5.) NoSqlRepository which persist meta data into NoSql DB.

@Component
public class NoSqlRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(NoSqlRepository.class);
private final DocumentFormat documentFormat = DocumentFormat.JSON;
private static final String SEPARATOR = ",";

private static final ThreadLocal<MyLocalVariable> THREAD_LOCAL_VARIABLES = ThreadLocal.withInitial(() -> new MyLocalVariable());


static class MyLocalVariable {
    private JsonSchemaManager schemaManager = new JsonSchemaManager();
    private BasicBSONDecoder bsonDecoder = new BasicBSONDecoder();

    public JsonSchemaManager getSchemaManager() {
        return schemaManager;
    }

    public BasicBSONDecoder getBsonDecoder() {
        return bsonDecoder;
    }
}

private void checkSchemaIfAny(NoSqlEntity noSqlEntity, JsonNode entity) {
    if (noSqlEntity.isNotNullSchema()) {
        THREAD_LOCAL_VARIABLES.get().getSchemaManager().check(noSqlEntity, entity);
    }
}

public String saveEntity(NoSqlEntity noSqlEntity, JsonNode entity){
  // Before persisting payload into noSQL, validate payload against schema.
  this.checkSchemaIfAny(noSqlEntity,entity);
}
// Other CURD methods here...
}

Upvotes: 3

Martina
Martina

Reputation: 164

As far as I know Jackson can only produce schemas for given types, but not do validation. There is json-schema-validator but it is no longer maintained.

Upvotes: 6

Related Questions