JSON Schema:验证数字或null值

39 投票
2 回答
57955 浏览
提问于 2025-04-17 23:19

有没有办法让JSON模式中的某个属性既可以是数字,也可以是null

我正在构建一个API,其中有一个heading属性。这个属性可以是一个介于0(包括0)到360(不包括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"}
        ]
      }
    }
}

2 个回答

57

这里的窍门是使用类型数组。不要这样写:

"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
55

在draft-04版本中,你可以使用anyOf这个指令:

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

你也可以像Adam建议的那样使用"type": ["number", "null"],不过我觉得anyOf更简洁(只要你使用draft-04的实现),而且它把最小值和最大值的声明和数字直接关联起来。

声明:我对Python的实现不了解,我的回答是关于json schema的。

撰写回答