我如何需要一个字段或另一个字段或(其他两个字段之一)但不是全部?

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,并且只能提供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'

【讨论】:

【参考方案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_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。

【讨论】:

以上是关于我如何需要一个字段或另一个字段或(其他两个字段之一)但不是全部?的主要内容,如果未能解决你的问题,请参考以下文章

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

如何在需要时仅提交隐藏/显示字段数据之一 - Laravel

如何使用反射设置另一个结构的字段之一的结构字段值?

Zend输入过滤器“无论是”还是“场景都不可能?

如何正确查询列表(或另一个 ManyToManyField)中所有对象的 ManyToManyField?

如何不重复字段声明(Selenium)