我如何需要一个或另一个领域或(另外两个)中的一个但不是全部?

时间:2014-06-03 19:38:00

标签: json validation jsonschema

我在提出JSON模式时遇到问题,该模式将验证JSON是否包含:

  • 仅限一个字段
  • 仅限其他字段
  • (仅限其他两个字段之一)

但是当它们的倍数存在时不匹配。

具体来说,我需要一个

  • copyAll
  • fileNames
  • matchesFiles和/或doesntMatchFiles

要验证,但我不想接受超过那个时间。

这是我到目前为止所得到的:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "required": [ "unrelatedA" ],
    "properties": {
    "unrelatedA": {
        "type": "string"
    },
    "fileNames": {
        "type": "array"
    },
    "copyAll": {
        "type": "boolean"
    },
    "matchesFiles": {
        "type": "array"
    },
    "doesntMatchFiles": {
        "type": "array"
        }
    },
    "oneOf": [
         {"required": ["copyAll"], "not":{"required":["matchesFiles"]}, "not":{"required":["doesntMatchFiles"]}, "not":{"required":["fileNames"]}},
         {"required": ["fileNames"], "not":{"required":["matchesFiles"]}, "not":{"required":["doesntMatchFiles"]}, "not":{"required":["copyAll"]}},
         {"anyOf": [
               {"required": ["matchesFiles"], "not":{"required":["copyAll"]}, "not":{"required":["fileNames"]}},
               {"required": ["doesntMatchFiles"], "not":{"required":["copyAll"]}, "not":{"required":["fileNames"]}}]}
    ]
} ;

这比我想要的更多。我希望这匹配以下所有内容:

{"copyAll": true, "unrelatedA":"xxx"}
{"fileNames": ["aab", "cab"], "unrelatedA":"xxx"}
{"matchesFiles": ["a*"], "unrelatedA":"xxx"}
{"doesntMatchFiles": ["a*"], "unrelatedA":"xxx"}
{"matchesFiles": ["a*"], "doesntMatchFiles": ["*b"], "unrelatedA":"xxx"}

但不匹配:

{"copyAll": true, "matchesFiles":["a*"], "unrelatedA":"xxx"}
{"fileNames": ["a"], "matchesFiles":["a*"], "unrelatedA":"xxx"}
{"copyAll": true, "doesntMatchFiles": ["*b"], "matchesFiles":["a*"], "unrelatedA":"xxx"}
{"fileNames": ["a"], "matchesFiles":["a*"], "unrelatedA":"xxx"}
{"unrelatedA":"xxx"}

我猜测那里有一些我不知道的东西 - 我想知道它是什么。

3 个答案:

答案 0 :(得分:76)

问题是“不”语义。 “不要求”并不意味着“包容禁止”。它只是意味着您不必添加它以验证该架构。

但是,您可以使用“oneOf”以更简单的方式满足您的规范。请记住,这意味着“只需要其中一个模式可以验证”。以下模式实现了您尝试解决的属性切换:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "required": [
        "unrelatedA"
    ],
    "properties": {
        "unrelatedA": {
            "type": "string"
        },
        "fileNames": {
            "type": "array"
        },
        "copyAll": {
            "type": "boolean"
        },
        "matchesFiles": {
            "type": "array"
        },
        "doesntMatchFiles": {
            "type": "array"
        }
    },
    "oneOf": [
        {
            "required": [
                "copyAll"
            ]
        },
        {
            "required": [
                "fileNames"
            ]
        },
        {
            "anyOf": [
                {
                    "required": [
                        "matchesFiles"
                    ]
                },
                {
                    "required": [
                        "doesntMatchFiles"
                    ]
                }
            ]
        }
    ]
}

答案 1 :(得分:0)

此答案与JSON模式无关,因此虽然可以带来解决此问题的另一种观点,但通常还是可以进行json验证,所以它有点偏离轨道。

重点是确切地声明您需要的结果:一个唯一的字段。考虑以下json模式:

JsonElement json =
    new Gson().toJsonTree(
        Map.of(
            "first_field", "vasya",
            "second_field", false,
            "third_field", 777,
            "unrelated", "Rinse"
        )
    );

假设您需要first_fieldsecond_fieldthird_field中的任何一个。第四场无关紧要。这是相应的验证对象的样子:

Result<SomeTestStructure> result =
    new UnnamedBlocOfNameds<SomeTestStructure>(
        List.of(
            new OneOf(
                "global",
                new ErrorStub("Only one of the fields must be present"),
                new AsString(
                    new Required(
                        new IndexedValue("first_field", json)
                    )
                ),
                new AsBoolean(
                    new Required(
                        new IndexedValue("second_field", json)
                    )
                ),
                new AsInteger(
                    new Required(
                        new IndexedValue("third_field", json)
                    )
                )
            ),
            new AsString(
                new IndexedValue("unrelated", json)
            )
        ),
        SomeTestStructure.class
    )
        .result();

首先,您声明一个由命名对象组成的未命名块;那么您说您需要三个元素中的一个成功的可验证元素。最后,您声明成功意味着什么。在这种情况下,成功就是简单存在。如果json有效,则创建SomeTestStructure类的对象:

assertTrue(result.isSuccessful());
assertEquals(
    new SomeTestStructure(777, "Rinse").thirdField(),
    result.value().raw().thirdField()
);

有关此方法和实现该方法的库的更多信息,请查看quick start entry

答案 2 :(得分:0)

如果值为 null 的属性与其不存在一样好,那么这样的事情可能是合适的。必须提供 commonProp,并且只能提供 xy 之一。

不过,您可能会收到一些类似的错误消息。

{
    $schema: 'http://json-schema.org/draft-07/schema#',
    type: 'object',
    required: ['commonProp'],

    oneOf: [
        {
            properties: {
                x: { type: 'number' },
                commonProp: { type: 'number' },
                y: {
                    type: 'null',
                    errorMessage: "should ONLY include either ('x') or ('y') keys. Not a mix.",
                },
            },
            additionalProperties: { not: true, errorMessage: 'remove additional property ${0#}' },
        },
        {
            properties: {
                y: { type: 'number' },
                commonProp: { type: 'number' },
                x: {
                    type: 'null',
                    errorMessage: "should ONLY include either ('x') or ('y') keys. Not a mix.",
                },
            },
            additionalProperties: { not: true, errorMessage: 'remove additional property ${0#}' },
        },
    ],
}
const model = { x: 0, y: 0, commonProp: 0 };

// ⛔️ ⛔️ ⛔️ ⛔️ ⛔️ ⛔️
// Model>y should ONLY include either ('x') or ('y') keys. Not a mix.
// Model>x should ONLY include either ('x') or ('y') keys. Not a mix.
const model = { x: 0, y: null, commonProp: 0 };

// ✅ ✅ ✅ ✅ ✅ ✅
const model = { x: 0 };

// ⛔️ ⛔️ ⛔️ ⛔️ ⛔️ ⛔️
// Model must have required property 'commonProp'