jsonschema - oneOf 关键字行为异常

jsonschema - oneOf keyword behaves unexpectedly

我正在尝试使用 jsonschema 3.0.1 验证 Python 中的 json 有效载荷,大致看起来像这样(简化了麻烦的部分):

{
    "request": {
        "topic": {
            "param1": "bleep beep topic",
            "param2": "bloop boop topic"
        },
        "message": {
            "param1": "bleep beep message",
            "param2": "bloop boop message"
        }
    }
}

一个有效的请求应该有两个字段:一个topic和匹配的message.

它们中的每一个都可以仅包含 param1两者 param1param2.

但它不能没有 topic 只有 param1 和 body 都有,也不能有 topic 有两者和 body 只有 param2:

因为一个节点的内容依赖于另一个节点的内容,我无法使用 dependencies 关键字或 if-then-else 构造,所以我尝试使用oneOf,并提供有效子模式的列表,其中引用了字段的 one_paramboth_params 版本,如下所示:

from jsonschema import validate

one_param = {
    "type": "object",
    "properties": {
        "param1": {
            "type": "string",
        }
    },
    "required": ["param1"]
}

both_params = {
    "type": "object",
    "properties": {
        "param1": {
            "type": "string",
        },
        "param2": {
            "type": "string",
        }
    },
    "required": ["param1", "param2"]
}

test_schema = {
    "type": "object",
    "properties": {
        "request": {
            "oneOf": [
                {
                    "type": "object",
                    "properties": {
                        "topic": one_param,
                        "message": one_param
                    },

                    "required": ["topic", "message"]
                },
                {
                    "type": "object",
                    "properties": {
                        "topic": both_params,
                        "message": both_params
                    },

                    "required": ["topic", "message"]
                }
            ],
        }
    }
}

验证器的行为不是我所期望的:它在两个参数的情况下都失败,并成功地验证了一个参数或不匹配参数的情况。

为什么我的验证模式没有像我解释的那样工作?


这是我为此编写的完整测试:

from jsonschema import validate

one_param = {
    "type": "object",
    "properties": {
        "param1": {
            "type": "string",
        }
    },
    "required": ["param1"]
}

both_params = {
    "type": "object",
    "properties": {
        "param1": {
            "type": "string",
        },
        "param2": {
            "type": "string",
        }
    },
    "required": ["param1", "param2"]
}

test_schema = {
    "type": "object",
    "properties": {
        "request": {
            "oneOf": [
                {
                    "type": "object",
                    "properties": {
                        "topic": one_param,
                        "message": one_param
                    },
                    "required": ["topic", "message"]
                },
                {
                    "type": "object",
                    "properties": {
                        "topic": both_params,
                        "message": both_params
                    },
                    "required": ["topic", "message"]
                }
            ],
        }
    }
}

good_1 = {
    "request": {
        "topic": {
            "param1": "bleep beep",
            "param2": "bloop boop"
        },
        "message": {
            "param1": "bleep beep message",
            "param2": "bloop boop message"
        }
    }
}

good_2 = {
    "request": {
        "topic": {
            "param1": "bleep beep"
        },
        "message": {
            "param1": "bleep beep message"
        }
    }
}

bad_1 = {
    "request": {
        "topic": {
            "param1": "bleep beep",
        },
        "message": {
            "param1": "bleep beep message",
            "param2": "bloop boop message with no matching topic"
        }
    }
}

bad_2 = {
    "request": {
        "topic": {
            "param1": "bleep beep",
            "param2": "bloop boop topic with no matching message"
        },
        "message": {
            "param1": "bleep beep message"
        }
    }
}

validate(good_1, test_schema)  # should validate
validate(good_2, test_schema)  # should validate
validate(bad_1, test_schema)  # should fail
validate(bad_2, test_schema)  # should fail

对于 oneOf,数组中的每一项(子模式)都应用于数据。如果您测试 oneOf 中的每个单独的子模式,会发生什么?

你会发现两者都是有效的!

您的 "one_param" 架构需要确保包含 param2 会导致它失败。您可以使用 additionalProperties 来执行此操作...

{
  "type": "object",
  "properties": {
    "param1": {
      "type": "string"
    }
  },
  "required": [
    "param1"
  ],
  "additionalProperties": false
}

我认为您假设只允许在 properties 中定义的属性,但事实并非如此,因此您还需要在 required.

中定义它们

您可以通过在 https://jsonschema.dev 上尝试架构来了解它是否有效。我已经使用更新的架构和实例预加载了 link。

顺便说一句,您可以使用 definitions$ref 来避免重复子模式,如果您希望将模式保存到单个 json 文件中。