java-schema的Java实例1

亚马逊SPAPI

# 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" );
1
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();
1
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))));
1
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);
1
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) &amp;&amp; parentSchema.getSchemaNode().get(SELECTORS).isArray()) {
            return Streams.stream(parentSchema.getSchemaNode().get(SELECTORS)).map(JsonNode::asText)
                    .filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        }
        return Sets.newHashSet();
    }
}
1
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()
            }
        };
    }
}

1
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()
            }
        };
    }
}
1
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