programing

둘 중 하나 또는 둘 중 하나(다른 두 필드 중 하나)를 모두 필요로 하지 않는 방법은 무엇입니까?

newstyles 2023. 3. 15. 19:27

둘 중 하나 또는 둘 중 하나(다른 두 필드 중 하나)를 모두 필요로 하지 않는 방법은 무엇입니까?

JSON에 다음 중 하나가 포함되어 있는지 검증하는 JSON 스키마를 작성하는데 문제가 있습니다.

  • 한 필드만
  • 다른 필드만
  • (기타 2개의 필드 중 하나)

하지만 그 배수가 존재할 때는 일치하지 않습니다.

제 경우엔, 특히 다음 중 하나를 원합니다.

  • 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"}

내가 놓치고 있는 게 분명해 - 그게 뭔지 알고 싶어

문제는 "not" 의미론입니다."not required"는 "disclused forgidd"를 의미하지 않습니다즉, 스키마를 검증하기 위해 추가할 필요가 없습니다.

그러나 "One Of"를 사용하면 사양을 더 쉽게 충족할 수 있습니다.이는 "이러한 스키마 중 하나만 검증할 수 있다"는 것을 의미합니다.다음 스키마는 해결하려는 속성 전환을 달성합니다.

{
    "$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"
                    ]
                }
            ]
        }
    ]
}

값이 다음과 같은 속성인 경우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'

코멘트에서 @Tomeamis가 지적한 바와 같이 불필요한 조합은 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"]}
                   ]
               }
           }]
     }
]
}

자세한 내용은 이쪽

재산의 존재를 금지하기 위해 다음과 같이 할 수도 있다.

{
    "properties": {
        "x": false
    }
}

회답에 기재된 바와 같이

파티에는 조금 늦었지만, 스키마 내에서 기능하고 재사용 가능한 솔루션을 오늘 구현했습니다.

컨텍스트의 경우 이름으로 입력해야 하는 필드가 여러 개 있었지만 값이 비어 있거나 다른 조건에 따라 존재해야 할 수 있습니다.


재사용 가능한 TypeScript 메서드는 다음과 같습니다.

// SchemaLogic.ts

import { Schema } from "jsonschema";

/**
 * A required string property with a minimum length of 0.
 */
export const StringValue = { type: "string", required: true, minLength: 0 };
/**
 * A required string property with a minimum length of 1.
 */
export const NonEmptyStringValue = { type: "string", required: true, minLength: 1 };

/**
 * Provides the option to submit a value for one of the two
 * property names provided. If one of the properties is
 * submitted with a truthy string value, then the other will
 * not be required to have a value. If neither are submitted
 * with a truthy value, then both will return an error
 * message saying that the minimum length requirement has
 * not been met.
 *
 * **NOTE:**
 *  1. this only works with string properties that are
 *     not restricted to a certain set of values or a
 *     regex-validated format
 *  1. this must be used inside an `allOf` array
 *
 * @param propertyNames the names of the properties
 * @returns a {@link Schema} that creates a conditional
 *  requirement condition between the two fields
 */
export const eitherOr = (propertyNames: [string, string]): Schema => {
    return {
        if: { properties: { [propertyNames[0]]: NonEmptyStringValue } },
        then: { properties: { [propertyNames[1]]: StringValue } },
        else: {
            if: { properties: { [propertyNames[1]]: NonEmptyStringValue } },
            then: { properties: { [propertyNames[0]]: StringValue } },
            else: {
                properties: {
                    [propertyNames[0]]: NonEmptyStringValue,
                    [propertyNames[1]]: NonEmptyStringValue,
                },
            },
        },
    };
};

그리고 여기 가장 기본적인 사용 방법의 예가 있습니다.여기에는 다음이 필요합니다.

  • xCode그리고.xDescription존재해야 하지만 하나의 값만 있으면 됩니다.
  • yCode그리고.yDescription존재해야 하지만 하나의 값만 있으면 됩니다.
import { eitherOr } from "./SchemaLogic";

const schema: Schema = {
    allOf: [eitherOr(["xCode", "xDescription"]), eitherOr(["yCode", "yDescription"])],
};

이러한 필드를 조건부로 필요로 하는 경우는, 다음과 같은 것을 사용할 수 있습니다.

const schema: Schema = {
    properties: {
        type: {
            type: ["string"],
            enum: ["one", "two", "three"],
            required: true,
        },
    },
    if: {
        // if the 'type' property is either "one" or "two"...
        properties: { type: { oneOf: [{ const: "one" }, { const: "two" }] } },
    },
    then: {
        // ...require values
        allOf: [eitherOr(["xCode", "xDescription"]), eitherOr(["yCode", "yDescription"])],
    },
};

주의:

에서 '하다'를 사용하는 additionalProperties: false【속성】션에 속성을 추가하여 정의해야 합니다.그렇지 않으면 필드가 존재해야 하는 동시에 추가 필드이기 때문에 허용되지 않습니다.

이것이 도움이 되기를 바랍니다!

이 답변은 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();

먼저 이름 있는 블록으로 구성된 이름 없는 블록을 선언한 다음 세 가지 요소 중 하나의 성공 가능한 요소가 필요하다고 말합니다.그리고 마지막으로, 당신은 성공이 무엇을 의미하는지 선언합니다.이 경우 성공하는 것은 단순히 존재하는 것이다.한 경우, .SomeTestStructure클래스가 생성됩니다.

assertTrue(result.isSuccessful());
assertEquals(
    new SomeTestStructure(777, "Rinse").thirdField(),
    result.value().raw().thirdField()
);

이 접근법과 이를 구현하는 라이브러리에 대한 자세한 내용은 빠른 시작 항목을 참조하십시오.

언급URL : https://stackoverflow.com/questions/24023536/how-do-i-require-one-field-or-another-or-one-of-two-others-but-not-all-of-them