我如何需要一个字段或另一个字段或(其他两个字段之一)但不是全部?
Posted
技术标签:
【中文标题】我如何需要一个字段或另一个字段或(其他两个字段之一)但不是全部?【英文标题】:How do I require one field or another or (one of two others) but not all of them? 【发布时间】:2014-07-24 06:47:21 【问题描述】:我无法提出一个 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"
我猜我缺少一些明显的东西 - 我想知道它是什么。
【问题讨论】:
我必须在外面使用 oneOf 标签作为父标签和里面的属性,这满足了我的要求。 medium.com/@dheerajkumar_95579/… 【参考方案1】:问题在于“非”语义。 “不需要”并不意味着“禁止包含”。这只是意味着您不必为了验证该架构而添加它。
但是,您可以使用“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"
]
]
]
【讨论】:
“不需要”如何不意味着禁止包含? IMO,如果您恰好有一个“不需要”元素,那么如果该元素存在,则“需要”成功,“不”因此失败。请注意,在 OP 的示例中,一个对象中有多个“非”键,这可能会导致一些奇怪的行为。此外,如果您在“不需要”子句中有多个元素,则仅表示只有 all 元素都存在时验证才会失败。然而,这可以通过使用“allOf”来规避。如果我错了,请纠正我。 @Tomeamis "not required" 的意思是"可能存在也可能不存在",而"inclusion denied" 的意思是"不得存在",不一样 @Mark 用简单的英语,你是对的。在 JSON 模式中,not
元素内的 required
元素(其数组内有一个字符串)实际上意味着 required
的参数是被禁止的。尝试针对此架构:"$schema":"http://json-schema.org/draft-07/schema#","not":"required":["forbidden"]
验证此 JSON:"forbidden":"asd"
,您将看到验证失败。【参考方案2】:
如果值为null
的属性与它不存在一样好,那么这样的东西可能是合适的。必须提供commonProp
,并且只能提供x
或y
之一。
您可能会收到一些类似的错误消息。
$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'
【讨论】:
【参考方案3】:正如@Tomeamis 在 cmets 中指出的那样,不需要的组合在 json 模式中意味着“禁止”。但是,您不应该重复“not”关键字(我真的不知道为什么)。相反,你应该
"$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":
"anyOf": [
"required":["matchesFiles"],
"required":["doesntMatchFiles"],
"required":["fileNames"]
]
,
"required": [
"fileNames"
],
"not":
"anyOf": [
"required":["matchesFiles"],
"required":["doesntMatchFiles"],
"required":["copyAll"]
]
,
"anyOf": [
"required": ["matchesFiles"],
"not":
"anyOf": [
"required":["fileNames"],
"required":["copyAll"]
]
,
"required": ["doesntMatchFiles"],
"not":
"anyOf": [
"required":["fileNames"],
"required":["copyAll"]
]
]
]
更多详情here
要禁止属性的存在,也可以这样做
"properties":
"x": false
如答案here中所述
【讨论】:
【参考方案4】:这个答案与 JSON 架构无关,所以它有点偏离轨道,虽然它可以带来解决这个问题的另一个视角,以及一般的 json 验证。
关键是要以声明的方式准确地表达您所需要的结果:唯一存在的单个字段。考虑以下 json 模式:
JsonElement json =
new Gson().toJsonTree(
Map.of(
"first_field", "vasya",
"second_field", false,
"third_field", 777,
"unrelated", "Rinse"
)
);
假设您需要first_field
、second_field
和third_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。
【讨论】:
以上是关于我如何需要一个字段或另一个字段或(其他两个字段之一)但不是全部?的主要内容,如果未能解决你的问题,请参考以下文章
我如何需要一个或另一个领域或(另外两个)中的一个但不是全部?
如何在需要时仅提交隐藏/显示字段数据之一 - Laravel