![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.solr.common.util.JsonSchemaValidator Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.solr.common.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
* A very basic and lightweight json schema parsing and data validation tool. This custom tool is
* created because 1) we need to support non json inputs 2) to avoiding double parsing (this accepts
* an already parsed json as a map). It validates most aspects of json schema, but it is NOT A FULLY
* COMPLIANT JSON schema parser or validator. This validator borrow some design's idea from
* https://github.com/networknt/json-schema-validator
*/
public class JsonSchemaValidator {
private List> validators;
private static Set KNOWN_FNAMES =
new HashSet<>(
Arrays.asList("description", "documentation", "default", "additionalProperties"));
public JsonSchemaValidator(String jsonString) {
this((Map) Utils.fromJSONString(jsonString));
}
public JsonSchemaValidator(Map, ?> jsonSchema) {
this.validators = new ArrayList<>();
for (Map.Entry, ?> entry : jsonSchema.entrySet()) {
Object fname = entry.getKey();
if (KNOWN_FNAMES.contains(fname.toString())) continue;
Function, ?>, Validator>> initializeFunction =
VALIDATORS.get(fname.toString());
if (initializeFunction == null) throw new RuntimeException("Unknown key : " + fname);
this.validators.add(initializeFunction.apply(new Pair<>(jsonSchema, entry.getValue())));
}
}
// This is a heterogeneous type, there's probably improvements to be had by using some concrete
// types instead
static final Map, ?>, Validator>>> VALIDATORS = new HashMap<>();
static {
loadValidators();
}
@SuppressWarnings("unchecked")
private static void loadValidators() {
VALIDATORS.put("items", pair -> new ItemsValidator(pair.first(), (Map, ?>) pair.second()));
VALIDATORS.put("enum", pair -> new EnumValidator(pair.first(), (List) pair.second()));
VALIDATORS.put(
"properties",
pair -> new PropertiesValidator(pair.first(), (Map>) pair.second()));
VALIDATORS.put("type", pair -> new TypeValidator(pair.first(), pair.second()));
VALIDATORS.put(
"required", pair -> new RequiredValidator(pair.first(), (List) pair.second()));
VALIDATORS.put("oneOf", pair -> new OneOfValidator(pair.first(), (List) pair.second()));
}
public List validateJson(Object data) {
List errs = new ArrayList<>();
validate(data, errs);
return errs.isEmpty() ? null : errs;
}
boolean validate(Object data, List errs) {
if (data == null) return true;
for (Validator> validator : validators) {
if (!validator.validate(data, errs)) {
return false;
}
}
return true;
}
}
abstract class Validator {
Validator(Map, ?> schema, T properties) {}
;
abstract boolean validate(Object o, List errs);
}
enum Type {
STRING(String.class),
ARRAY(List.class),
NUMBER(Number.class),
INTEGER(Long.class) {
@Override
boolean isValid(Object o) {
if (super.isValid(o)) return true;
try {
Long.parseLong(String.valueOf(o));
return true;
} catch (NumberFormatException e) {
return false;
}
}
},
LONG(Long.class) {
@Override
boolean isValid(Object o) {
if (super.isValid(o)) return true;
try {
Long.parseLong(String.valueOf(o));
return true;
} catch (NumberFormatException e) {
return false;
}
}
},
BOOLEAN(Boolean.class) {
@Override
boolean isValid(Object o) {
if (super.isValid(o)) return true;
try {
Boolean.parseBoolean(String.valueOf(o));
return true;
} catch (NumberFormatException e) {
return false;
}
}
},
ENUM(List.class),
OBJECT(Map.class),
NULL(null),
UNKNOWN(Object.class);
final Class> type;
Type(Class> type) {
this.type = type;
}
boolean isValid(Object o) {
if (type == null) return o == null;
return type.isInstance(o);
}
}
class TypeValidator extends Validator
© 2015 - 2025 Weber Informatics LLC | Privacy Policy