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.
The simplest way to check if JSON is valid is to load the JSON into a JObject or JArray and then use the IsValid(JToken, JsonSchema) method with the JSON Schema. To get validation error messages, use the IsValid(JToken, JsonSchema, IList<String> ) or Validate(JToken, JsonSchema, ValidationEventHandler) overloads.
JSON Schema validation asserts constraints on the structure of instance data. An instance location that satisfies all asserted constraints is then annotated with any keywords that contain non-assertion information, such as descriptive metadata and usage hints.
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...
}
Just stumbled about https://github.com/leadpony/justify another implementation of a validator for json schema, also more recent draft versions. (7,6,4)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With