【问题标题】:JSON Schema: validate a number-or-null valueJSON Schema:验证数字或空值
【发布时间】:2014-04-29 04:32:13
【问题描述】:

有没有办法让 JSON 架构属性成为数字或 null

我正在构建一个包含 heading 属性的 API。可以是介于 0(含)和 360(不含)之间的数字,也可以是 null,因此以下输入都可以:

{"heading": 5}
{"heading": 0}
{"heading": null}
{"heading": 12}
{"heading": 120}
{"heading": null}

并且以下输入是错误的:

{"heading": 360}
{"heading": 360.1}
{"heading": -5}
{"heading": false}
{"heading": "X"}
{"heading": 1200}
{"heading": false}

附录:

anyOf 显然是正确的答案。为了清楚起见,添加完整的架构。

架构

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "heading": {
        "anyOf": [
          {"type": "number"},
          {"type": "null"}
        ]
      }
    }
}

【问题讨论】:

    标签: python json api validation jsonschema


    【解决方案1】:

    诀窍是使用类型数组。而不是:

    "type": "number"
    

    用途:

    "type": ["number", "null"]
    

    以下代码强制执行数字或空策略,如果值为数字,则加上数字限制:

    from jsonschema import validate
    from jsonschema.exceptions import ValidationError
    import json
    
    schema=json.loads("""{
      "$schema": "http://json-schema.org/schema#",
      "description": "Schemas for heading: either a number within [0, 360) or null.",
      "title": "Tester for number-or-null schema",
      "properties": {
        "heading": {
          "type": ["number", "null"],
          "exclusiveMinimum": false,
          "exclusiveMaximum": true,
          "minimum": 0,
          "maximum": 360
        }
      }
    }""")
    
    inputs = [
    {"heading":5}, {"heading":0}, {"heading":360}, {"heading":360.1},
    {"heading":-5},{"heading":None},{"heading":False},{"heading":"X"},
    json.loads('''{"heading":12}'''),json.loads('''{"heading":120}'''),
    json.loads('''{"heading":1200}'''),json.loads('''{"heading":false}'''),
    json.loads('''{"heading":null}''')
    ]
    
    for input in inputs:
        print "%-30s" % json.dumps(input),
        try:
            validate(input, schema)
            print "OK"
        except ValidationError as e:
            print e.message
    

    这给出了:

    {"heading": 5}                 OK
    {"heading": 0}                 OK
    {"heading": 360}               360.0 is greater than or equal to the maximum of 360
    {"heading": 360.1}             360.1 is greater than or equal to the maximum of 360
    {"heading": -5}                -5.0 is less than the minimum of 0
    {"heading": null}              OK
    {"heading": false}             False is not of type u'number', u'null'
    {"heading": "X"}               'X' is not of type u'number', u'null'
    {"heading": 12}                OK
    {"heading": 120}               OK
    {"heading": 1200}              1200.0 is greater than or equal to the maximum of 360
    {"heading": false}             False is not of type u'number', u'null'
    {"heading": null}              OK
    

    【讨论】:

    • 这对于可以将 null 作为值的枚举如何工作? {"enum": ["one", "two", "null"]} 似乎不起作用,至少在 jsonschema 的 python 实现中。
    • 提防"null"null
    【解决方案2】:

    在草案 04 中,您将使用 anyOf 指令:

    {
      "anyOf": [
        {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "exclusiveMaximum": true
        },
        {
          "type": "null"
        }
      ]
    }
    

    您也可以按照 Adam 的建议使用 "type": ["number", "null"],但我认为 anyOf 更简洁(只要您使用 draft-04 实现),并且绑定了最小和最大声明明确的数字。

    免责声明:我对 python 实现一无所知,我的答案是关于 json 架构。

    【讨论】:

    • 不幸的是,2.0 规范似乎不支持anyOf。 :(
    【解决方案3】:

    此答案不考虑 JSON Schema,但如果您想在未来考虑一些替代方案,它会很有用。

    如果你只希望几个可验证元素中的一个成功,你可以直接写这个:

    Validatable<?> validatableElement =
        new OneOf(
            "global",
            new ErrorStub("This field should be either integer, or null, or absent at all"),
            new AsInteger(
                new Required(
                    new IndexedValue("header", json)
                )
            ),
            new IsAbsent<>(
                new IndexedValue("header", json)
            )
        );
    

    如果有一个值并且它是整数,那么结果将如下:

    Result<?> result = validatableElement.result();
    assertTrue(result.isSuccessful());
    assertEquals(
        24,
        result.value().raw()
    );
    

    如果没有标头通过或者它为空,那么结果将如下所示:

    assertTrue(result.isSuccessful());
    assertFalse(result.value().isPresent());
    

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

    【讨论】:

    • 这个六年前的问题被标记为[python],但给定的代码看起来很像[java]
    • 糟糕,我的错!不管怎样,python 端口在路上!
    猜你喜欢
    • 2021-05-06
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 2021-04-18
    • 2019-10-18
    相关资源
    最近更新 更多