-
Notifications
You must be signed in to change notification settings - Fork 2
Java
The main driver for the Java implementation of Concordia is the Concordia class.
The constructor is responsible for consuming some schema, either through directly instantiated objects or through serialization from Jackson. It also takes a ValidationController object which can either be the default, static one that is pre-built in the ValidationController class or a custom one that includes custom schema and/or data validation.
The validateData function validates data for the schema that was used to build the Concordia object, may be called any number of times, and is thread safe.
Custom validation is performed by any class that extends the TypeValidator<S extends Schema> and/or DataValidator<S extends Schema>. Here is an example validator that verifies that, if an "enum" field exists, it is an array of strings and that all data points for that string schema are strings:
public class EnumValidator
implements SchemaValidator<StringSchema>, DataValidator<StringSchema> {
/**
* The schema field name for string schemas that may be present and, if so,
* must be an array of strings that are valid values for this data.
*/
public static final String ENUM_SCHEMA_FIELD = "enum";
/**
* Verifies that if {@link #ENUM_SCHEMA_FIELD} exists, it is an array of
* strings.
*/
@Override
public void validate(
final StringSchema schema,
final ValidationController controller)
throws ConcordiaException {
// Attempt to get the enum definition.
Object enumField = schema.getAdditionalFields().get(ENUM_SCHEMA_FIELD);
// If the definition exists, validate it.
if(enumField != null) {
// It must be a JSON array.
if(enumField instanceof List) {
// Iterate through the elements.
Iterator<?> enumFieldIter =
((List<?>) enumField).iterator();
// Each element must be a string.
while(enumFieldIter.hasNext()) {
if(! (enumFieldIter.next() instanceof String)) {
throw
new ConcordiaException(
"An " +
ENUM_SCHEMA_FIELD +
" entry is not a string: " +
enumField.toString());
}
}
}
// It is not a JSON array.
else {
throw
new ConcordiaException(
"The " +
ENUM_SCHEMA_FIELD +
" field list must be a JSON array: " +
enumField.toString());
}
}
}
/**
* Verifies that any data point for this schema is one of the required enum
* values.
*/
@Override
public void validate(
final StringSchema schema,
final JsonNode data,
final ValidationController controller)
throws ConcordiaException {
// If the data is null, we can ignore it because that value shouldn't
// be in our list. Null will be caught by our default, required
// validation, which will only allow this if the schema defines this
// field as optional.
if((data == null) || (data instanceof NullNode)) {
return;
}
// Get the value of this node.
// We can also safely assume that this will return us a non-null value
// as our default, required validation would have first run to ensure
// that it is a TextNode.
String value = data.textValue();
// Attempt to get the enum definition.
Object enumField = schema.getAdditionalFields().get(ENUM_SCHEMA_FIELD);
// If the definition exists, validate it.
if(enumField != null) {
// We can safely cast here, because our validation above took care
// of this for us.
@SuppressWarnings("unchecked")
List<String> enumFieldIter = (List<String>) enumField;
// Check each of our allowed values against the given value.
if(! enumFieldIter.contains(value)) {
// If one was not found, throw an exception.
throw
new ConcordiaException(
"The value, '" +
value +
"', is not in our list of acceptable values: " +
enumField.toString());
}
}
}
}
To test this, a custom validator would need to add an instance of the EnumValidator
to a ValidationController
that was used to build a Concordia
object:
// Build the input stream of data to use.
InputStream schemaStream =
new ByteArrayInputStream(
("{" +
"\"type\":\"object\"," +
"\"fields\":[" +
"{" +
"\"name\":\"noEnum\"," +
"\"type\":\"string\"" +
"}," +
"{" +
"\"name\":\"yesEnum\"," +
"\"type\":\"string\"," +
"\"enum\":[" +
"\"this is valid\"" +
"]" +
"}" +
"]" +
"}")
.getBytes());
// Build the validation controller with our custom validation.
Builder controllerBuilder = new ValidationController.Builder();
controllerBuilder
.addValidator(StringSchema.class, new EnumValidator());
ValidationController controller = controllerBuilder.build();
// Create the Concordia object which validates that the "enum" field is
// valid.
Concordia test = new Concordia(schemaStream, controller);
// Create some data to test.
JsonNode data =
(new MappingJsonFactory()).createJsonParser(
"{" +
"\"noEnum\":\"this can be anything\"," +
"\"yesEnum\":\"this is valid\"" +
"}")
.readValueAs(JsonNode.class);
// Validate the data.
test.validateData(data);
}