当前位置:网站首页>Networknt:: JSON schema validator source code appreciation
Networknt:: JSON schema validator source code appreciation
2022-06-23 14:11:00 【rhyechen】
Json Is a self explanatory language , Widely used in request protocols 、 The configuration file 、 Format specification and other scenarios . For constraint Json data format , Need to use another special Json data -- JsonSchema standard .
Official website https://json-schema.org/ Recommended. snow、vert.x、everit-org、networknt Several kinds Java Realization , among networknt It has been widely used with excellent performance , Today, let's analyze networknt Of Java Version implementation .
Code warehouse : https://github.com/networknt/json-schema-validator edition (1.0.64)
Various prefabricated validator Inherit from BaseJsonValidator, There are two important ways walk(...) and validate(...) . Both functions and return values are similar ,walk Method support in validate Method to call the registered PreWalkListeners/PostWalkListeners Section method , You can implement some custom functions in it , For example, printing logs .
Various prefabricated validator Need to be in ValidatorTypeCode Register in , Let's take a look ValidatorTypeCode Part of the declared core code :
public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
...
MAXIMUM("maximum", "1011", new MessageFormat(I18nSupport.getString("maximum")), MaximumValidator.class, 15),
...
IF_THEN_ELSE("if", "1037", null, IfValidator.class, 12),
...
private ValidatorTypeCode(String value, String errorCode, MessageFormat messageFormat, Class validator, long versionCode) {
this.value = value;
this.errorCode = errorCode;
this.messageFormat = messageFormat;
this.errorCodeKey = value + "ErrorCode";
this.validator = validator;
this.versionCode = versionCode;
this.customMessage = null;
}
...
public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws Exception {
if (validator == null) {
throw new UnsupportedOperationException("No suitable validator for " + getValue());
}
// if the config version is not match the validator
@SuppressWarnings("unchecked")
Constructor<JsonValidator> c = ((Class<JsonValidator>) validator).getConstructor(
new Class[]{String.class, JsonNode.class, JsonSchema.class, ValidationContext.class});
return c.newInstance(schemaPath + "/" + getValue(), schemaNode, parentSchema, validationContext);
}
...
}Have you found any problems ?
Although each check element is validator, But after registering to Factory Is actually being ValidatorTypeCode ( A kind of Keyword Realization ) Packed one by one . When it is necessary to expand into validator when , By registering class Type to find the constructor with fixed signature and instantiate .
Don't worry about performance ,validators Be being JsonSchema Lazy load and hold , It will only be initialized once and will accompany the whole JsonSchema The entire life cycle of the instance will not change .
That is how to determine what needs to be loaded validators Well ? Here we need to mention Json-Schema The Syntax version of ( see https://json-schema.org/specification-links.html).networknt At present, we support v4、v6、v7、v2019-09 edition , Each version will specify specific check keywords .
ValidatorTypeCode There is one of them. versionCode attribute , Every validator At the time of registration, you have indicated your version . For easy storage versionCode The applicable version of... Is marked by a binary mask . for example MAXIMUM:15 = 8 + 4 + 2 + 1 ( Applicable to all versions ),IF_THEN_ELSE:12 = 8 + 4 ( apply v2019-09 and v7 ).
The registration information for each Syntax version is in JsonMetaSchema in , With v6 For example :
private static class V6 {
private static String URI = "https://json-schema.org/draft-06/schema";
// Draft 6 uses "$id"
private static final String ID = "$id";
public static final List<Format> BUILTIN_FORMATS = new ArrayList<Format>(JsonMetaSchema.COMMON_BUILTIN_FORMATS);
static {
// add version specific formats here.
//BUILTIN_FORMATS.add(pattern("phone", "^\\+(?:[0-9] ?){6,14}[0-9]$"));
}
public static JsonMetaSchema getInstance() {
return new Builder(URI)
.idKeyword(ID)
.addFormats(BUILTIN_FORMATS)
.addKeywords(ValidatorTypeCode.getNonFormatKeywords(SpecVersion.VersionFlag.V6))
// keywords that may validly exist, but have no validation aspect to them
.addKeywords(Arrays.asList(
new NonValidationKeyword("$schema"),
new NonValidationKeyword("$id"),
new NonValidationKeyword("title"),
new NonValidationKeyword("description"),
new NonValidationKeyword("default"),
new NonValidationKeyword("definitions")
))
.build();
}
} It mainly consists of two parts :ValidatorTypeCode( The version corresponds to validators),NonValidationKeyword( System keyword corresponding to version ). Both types implement self Keyword keyword , Users can also use Keyword Implement custom dialect .
At the beginning, I talked about Json-Schema It's a special kind Json data , therefore validators The whole process of building is to json-schema tree Analytic process . Key code :
public JsonSchema extends BaseJsonValidator {
...
/**
* Please note that the key in {@link #validators} map is a schema path. It is
* used in {@link com.networknt.schema.walk.DefaultKeywordWalkListenerRunner} to derive the keyword.
*/
private Map<String, JsonValidator> read(JsonNode schemaNode) {
Map<String, JsonValidator> validators = new TreeMap<>(VALIDATOR_SORT);
if (schemaNode.isBoolean()) {
if (schemaNode.booleanValue()) {
final String customMessage = getCustomMessage(schemaNode, "true");
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "true", schemaNode, this, customMessage);
validators.put(getSchemaPath() + "/true", validator);
} else {
final String customMessage = getCustomMessage(schemaNode, "false");
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "false", schemaNode, this, customMessage);
validators.put(getSchemaPath() + "/false", validator);
}
} else {
Iterator<String> pnames = schemaNode.fieldNames();
while (pnames.hasNext()) {
String pname = pnames.next();
JsonNode nodeToUse = pname.equals("if") ? schemaNode : schemaNode.get(pname);
String customMessage = getCustomMessage(schemaNode, pname);
JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, nodeToUse, this, customMessage);
if (validator != null) {
validators.put(getSchemaPath() + "/" + pname, validator);
if (pname.equals("required")) {
requiredValidator = validator;
}
}
}
}
return validators;
}
...
}It seems only on the first floor schema Did validators Generation , Actually properties And so on , Each of them has its own substructure validators ( See PropertiesValidator).
The above is the definition of various syntax keywords and validators Registration process , Here's an atom checker MaxItemsValidator For example , Analyze the inspection process in detail .
// schema Configuration style : "maxItems": 5
public class MaxItemsValidator extends BaseJsonValidator implements JsonValidator {
private static final Logger logger = LoggerFactory.getLogger(MaxItemsValidator.class);
private final ValidationContext validationContext;
private int max = 0;
public MaxItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.MAX_ITEMS, validationContext);
if (schemaNode.canConvertToExactIntegral()) {
max = schemaNode.intValue();
}
this.validationContext = validationContext;
parseErrorCode(getValidatorType().getErrorCodeKey());
}
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
debug(logger, node, rootNode, at);
if (node.isArray()) {
if (node.size() > max) {
return Collections.singleton(buildValidationMessage(at, "" + max));
}
} else if (this.validationContext.getConfig().isTypeLoose()) {
if (1 > max) {
return Collections.singleton(buildValidationMessage(at, "" + max));
}
}
return Collections.emptySet();
}
}Generate validator Instance when calling the constructor schemaPath and schemaNode , Get the maximum number of configured elements and save them in max Properties of the .
When traversing the data to the corresponding node Node time , Will check the corresponding validators, find maxitems And call validate Method , This method first judges the current node Is it array type ,true Then continue to judge whether the array length exceeds the maximum limit , Otherwise, the wrong type will be thrown .
When a hit error occurs, the public buildValidationMessage (String at, String... arguments) Method .at Used to indicate that the current error occurred in json tree Specific hierarchical location of , arguments Is used to fill in ValidatorTypeCode Declarative MessageFormat Parameter placeholder for .
maxItems = {0}: there must be a maximum of {1} items in the arrayAfter filling, it is : { route } : The number of elements under cannot exceed the maximum {max} individual .
It should be noted that the error is in the form of a set Set Form return of ,ValidationMessage Of equals Methods have also been specially treated :
public class ValidationMessage {
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ValidationMessage that = (ValidationMessage) o;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
if (code != null ? !code.equals(that.code) : that.code != null) return false;
if (path != null ? !path.equals(that.path) : that.path != null) return false;
if (details != null ? !details.equals(that.details) : that.details != null) return false;
if (!Arrays.equals(arguments, that.arguments)) return false;
return !(message != null ? !message.equals(that.message) : that.message != null);
}
...
}notes : from 1.0.58 The version began to add multiple languages ( in / english ) Error reporting support for , And added the function of customizing errors , Let's not go into details here .
That's right networknt Of json-schema-validator Analysis of core source code .
To sum up, there are two main points :
1、 Atomization of the checker , In the later stage, it can be nested deeply through configuration and combination .
2、 Tree traversal ,Schema The initialization phase recursively generates validators ,Data Recursive triggers validators.
There are many things worth learning about design, such as the nesting of atomic components 、 Faceting listener Buried point .
But there is also a lot of hard coding in the code , With reflection keyword To validator Mapping is not particularly elegant .
As the version iterates , Gradually added i18n、customMessage And more new features , I will continue to follow up on some new features , Welcome to exchange and study together .
边栏推荐
- The company has only one test, but the leader asked me to operate 1000 mobile numbers at the same time
- Loss, duplication and backlog of message queues
- leetcode:242. 有效的字母异位词
- 『忘了再学』Shell流程控制 — 39、特殊流程控制语句
- Intel ® extensions for pytorch* accelerate pytorch
- Flutter Clip剪裁组件
- Assembly language interrupt and external device operation --06
- Vulnhub target os-hacknos-1
- 【深入理解TcaplusDB技术】单据受理之表管理
- 通过 OpenVINO Model Server和 TensorFlow Serving简化部署
猜你喜欢

Shutter clip clipping component

实战 | 如何制作一个SLAM轨迹真值获取装置?

边缘和物联网学术资源
![[Yunzhou said live room] - digital security special session will be officially launched tomorrow afternoon](/img/56/a6a9fbba0a9fc212883b469bb857c5.png)
[Yunzhou said live room] - digital security special session will be officially launched tomorrow afternoon

The way out after the development of Internet technology -- the birth of IVX

Intelligent digital signage solution

Binding events of wechat applet in wx:for

白皮书丨英特尔携手知名RISC-V工具提供商Ashling,着力扩展多平台RISC-V支持

From the establishment to the actual combat of the robotframework framework, I won't just read this learning note

Multi-Camera Detection of Social Distancing Reference Implementation
随机推荐
quartus调用&设计D触发器——仿真&时序波验证
【深入理解TcaplusDB技术】 Tmonitor模块架构
Sqlserver2008r2 failed to install DTS component
爱思唯尔-Elsevier期刊的校稿流程记录(Proofs)(海王星Neptune)(遇到问题:latex去掉章节序号)
From the establishment to the actual combat of the robotframework framework, I won't just read this learning note
[deeply understand tcapulusdb technology] tmonitor background one click installation
OpenVINOTM 2022.1中AUTO插件和自动批处理的最佳实践
KS007基于JSP实现人个人博客系统
When did the redo log under InnoDB in mysql start to perform check point disk dropping?
How deci and Intel can achieve up to 16.8x throughput improvement and +1.74% accuracy improvement on mlperf
Common methods and descriptions of beanstalk
One way linked list implementation -- counting
KS003基于JSP和Servlet实现的商城系统
Shutter clip clipping component
Understand ADT and OOP
Win10 64位系统如何安装SQL server2008r2的DTS组件?
腾讯云TDSQL-C重磅升级,性能全面领跑云原生数据库市场
pyqt5之designer制作表格
实战监听Eureka client的缓存更新
父母-子女身高数据集的线性回归分析