# Java的验证器实现示例
对于Java应用程序,networknt/json-schema-validator (opens new window)库支持JSON Schema Draft 2019-09和自定义词汇.以下例子演示了如何利用[networknt/json-schema-validator](https //github.com/networknt/json-schema-validator)库来验证具有Amazon产品类型定义Meta-Schema实例的有效载荷.没有要求使用这个特定的库或示例实现.亚马逊不为第三方JSON Schema库提供技术支持,这仅作为一个例子提供.
# Schema Configuration
当使用networknt/json-schema-validator (opens new window)来验证带有自定义词汇的Amazon产品类型定义Meta-Schema的实例时,meta-schema被配置为JsonSchemaFactory
的一部分.
常量
// 亚马逊产品类型定义Meta-Schema.的id
String schemaId = "https://schemas.amazon.com/selling-partners/definitions/product-types/meta-schema/v1";
// 亚马逊产品类型定义的本地副本 Meta-Schema.
String metaSchemaPath = "./amazon-product-type-definition-meta-schema-v1.json";
// 亚马逊产品类型定义Meta-Schema.实例的本地拷贝
String luggageSchemaPath = "./luggage.json";
//只提供信息,不需要验证的关键词.
List<String> nonValidatingKeywords = ImmutableList.of("editable", "enumNames" );
2
3
4
5
6
7
8
9
10
11
配置Meta-Schema:
//亚马逊产品类型定义Meta-Schema从.扩展的标准JSON Schema 2019-09
JsonMetaSchema standardMetaSchema = JsonMetaSchema.getV201909();
//以标准JSON Schema 2019-09为蓝本建立亚马逊产品类型定义元模式.
//注册自定义关键词验证类(见下文).
JsonMetaSchema metaSchema = JsonMetaSchema.builder(SCHEMA_ID, standardMetaSchema)
.addKeywords(NON_VALIDATING_KEYWORDS.stream().map(NonValidationKeyword::new)
.collect(Collectors.toSet()))
.addKeyword(new MaxUniqueItemsKeyword())
.addKeyword(new MaxUtf8ByteLengthKeyword())
.addKeyword(new MinUtf8ByteLengthKeyword())
.build();
2
3
4
5
6
7
8
9
10
11
12
建立JsonSchemaFactory:
// URIFetcher将meta-schema引用路由到本地副本.
URIFetcher uriFetcher = uri -> {
// 使用元-schema的本地副本,而不是从网络上检索.
if (schemaId.equalsIgnoreCase(uri.toString())) {
return Files.newInputStream(Paths.get(metaSchemaPath));
}
// 对于其他模式,默认为现有的获取器.
return new URLFetcher().fetch(uri);
};
// 建立JsonSchemaFactory.
JsonSchemaFactory schemaFactory = new JsonSchemaFactory.Builder()
.defaultMetaSchemaURI(schemaId)
.addMetaSchema(standardMetaSchema)
.addMetaSchema(metaSchema)
.uriFetcher(uriFetcher, "https")
.build();
// 创建JsonSchema实例.
JsonSchema luggageSchema = schemaFactory.getSchema(new String(Files.readAllBytes(Paths.get(luggageSchemaPath))));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Payload验证
在Amazon产品类型定义Meta-Schema的实例作为JsonSchema
实例加载后,可以使用instance.验证有效载荷
//为payload创建一个JsonNode (这可以在代码中构建,从文件中读取,等等.).
JsonNode payload = new ObjectMapper().readValue(new File("./payload.json"), JsonNode.class)
//验证有效载荷并获得任何结果的验证信息.
Set<ValidationMessage> messages = luggageSchema.validate(payload);
2
3
4
5
如果没有返回验证信息,说明验证通过了.否则,检查验证信息以确定有效载荷的错误.
# 关键词验证
networknt/json-schema-validator (opens new window) 支持通过使用扩展AbstractKeyword
类并提供验证逻辑的类来验证自定义词汇.
See https://github.com/networknt/json-schema-validator/blob/master/doc/validators.md (opens new window).
下面的例子说明了 "AbstractKeyword "类的扩展,在Amazon产品类型定义Meta-Schema.的实例中验证了自定义词汇
# MaxUniqueItemsKeyword
Class
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.networknt.schema.AbstractJsonValidator;
import com.networknt.schema.AbstractKeyword;
import com.networknt.schema.CustomErrorMessageType;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonValidator;
import com.networknt.schema.validationContext;
import com.networknt.schema.validationMessage;
import org.apache.commons.lang3.StringUtils;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* "maxUniqueItems "关键词的验证器实例.
*/
public class MaxUniqueItemsKeyword extends AbstractKeyword {
private static final MessageFormat ERROR_MESSAGE_FORMAT = new MessageFormat("每个选择器的组合"
+ "值只能出现{1}次.以下选择器值组合出现的次数过多{2}");
private static final String KEYWORD = "maxUniqueItems";
private static final String SELECTORS = "selectors";
public MaxUniqueItemsKeyword() {
super(KEYWORD);
}
@Override
public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidationContext validationContext) {
//只在提供的模式值是数字时才处理.
if (!JsonNodeType.NUMBER.equals(schemaNode.getNodeType())) {
返回null
}
int maxUniqueItems = schemaNode.asInt();
// 获取方案元素上配置的选择器属性,如果它们存在的话. 否则,这个验证器
//默认为使用所有属性.
Set<String> selectors = getSelectorProperties(parentSchema);
return new AbstractJsonValidator(this.getValue()) {
覆盖
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
// 只在节点为数组时进行处理,因为选择器和唯一项目不适用于其他数据
// types.
如果(node.isArray()) {
// 创建一个每个项目属性的属性-value map (selectors) 并计算每个组合的//出现次数
//每个组合的出现次数.
Map<Map<String, String>, Integer> uniqueItemCounts = Maps.newHashMap();
node.forEach(instance -> {
//只处理属于对象的实例.
如果(instance.isObject()) {
Map<String, String> uniqueKeys = Maps.newHashMap();
Iterator<Map.Entry<String, JsonNode>> fieldIterator = instance.fields();
while (fieldIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = fieldIterator.next();
// 如果没有配置选择器,总是添加. 否则只有在属性是
//一个选择器.
如果(selectors.isEmpty() || selectors.contains(entry.getKey()) {
uniqueKeys.put(entry.getKey(), entry.getValue().asText());
}
}
// 迭代计数并放入计数 map.
int count = uniqueItemCounts.getOrDefault(uniqueKeys, 0) + 1;
uniqueItemCounts.put(uniqueKeys, count);
}
});
// 找到第一个有太多实例的选择器组合.
Optional<Map<String, String>> uniqueKeysWithTooManyItems = uniqueItemCounts.entrySet()
.stream().filter(entry -> entry.getValue() > maxUniqueItems).map(Map.Entry::getKey)
.findFirst();
// 如果一个选择器组合有太多的实例,返回一个失败的验证.
if (uniqueKeysWithTooManyItems.isPresent()) {
return fail(CustomErrorMessageType.of(KEYWORD, ERROR_MESSAGE_FORMAT), at,
Integer.toString(maxUniqueItems), uniqueKeysWithTooManyItems.get().toString());
}
返回pass()
}
};
}
private Set<String> getSelectorProperties(JsonSchema parentSchema) {
if (parentSchema.getSchemaNode().has(SELECTORS) && parentSchema.getSchemaNode().get(SELECTORS).isArray()) {
return Streams.stream(parentSchema.getSchemaNode().get(SELECTORS)).map(JsonNode::asText)
.filter(StringUtils::isNotBlank).collect(Collectors.toSet());
}
return Sets.newHashSet();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# MaxUtf8ByteLengthKeyword
Class
package com.amazon.spucs.tests.keywords;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.networknt.schema.AbstractJsonValidator;
import com.networknt.schema.AbstractKeyword;
import com.networknt.schema.CustomErrorMessageType;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonValidator;
import com.networknt.schema.validationContext;
import com.networknt.schema.validationMessage;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Set;
/**
* "maxUtf8ByteLength "关键字的验证器实例.
*/
public class MaxUtf8ByteLengthKeyword extends AbstractKeyword {
private static final MessageFormat ERROR_MESSAGE_FORMAT =
new MessageFormat("值必须小于或等于{1}字节的长度.")
private static final String KEYWORD = "maxUtf8ByteLength";
public MaxUtf8ByteLengthKeyword() {
super(KEYWORD);
}
@Override
public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidationContext validationContext) {
//只在提供的模式值是数字时才处理.
if (!JsonNodeType.NUMBER.equals(schemaNode.getNodeType())) {
返回null
}
int maxUtf8ByteLength = schemaNode.asInt();
return new AbstractJsonValidator(this.getValue()) {
覆盖
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
//获取字符串的值并评估其长度,单位为字节.
String value = node.asText();
如果(value.getBytes(StandardCharsets.UTF_8).length > maxUtf8ByteLength) {
return fail(CustomErrorMessageType.of(KEYWORD, ERROR_MESSAGE_FORMAT), at,
Integer.toString(maxUtf8ByteLength));
}
返回pass()
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# MinUtf8ByteLengthKeyword
Class
package com.amazon.spucs.tests.keywords;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.networknt.schema.AbstractJsonValidator;
import com.networknt.schema.AbstractKeyword;
import com.networknt.schema.CustomErrorMessageType;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonValidator;
import com.networknt.schema.validationContext;
import com.networknt.schema.validationMessage;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Set;
/**
* "minUtf8ByteLength "关键字的验证器实例.
*/
public class MinUtf8ByteLengthKeyword extends AbstractKeyword {
private static final MessageFormat ERROR_MESSAGE_FORMAT =
new MessageFormat("值必须大于或等于{1}字节的长度.")
private static final String KEYWORD = "minUtf8ByteLength";
public MinUtf8ByteLengthKeyword() {
super(KEYWORD);
}
@Override
public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidationContext validationContext) {
//只在提供的模式值是数字时才处理.
if (!JsonNodeType.NUMBER.equals(schemaNode.getNodeType())) {
返回null
}
int minUtf8ByteLength = schemaNode.asInt();
return new AbstractJsonValidator(this.getValue()) {
覆盖
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
//获取字符串的值并评估其长度,单位为字节.
String value = node.asText();
如果(value.getBytes(StandardCharsets.UTF_8).length < minUtf8ByteLength) {
return fail(CustomErrorMessageType.of(KEYWORD, ERROR_MESSAGE_FORMAT), at,
Integer.toString(minUtf8ByteLength));
}
返回pass()
}
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56