JSON Schema if-else condition complex scenario

2019-08-21 09:21发布

问题:

    {
        "policyHolder": {
            "fullName": "A"
        },
        "traveller": [
            {
                "fullName": "B",
                "relationship": "Spouse"
            },
            {
                "fullName": "A",
                "relationship": "My Self"
            }
        ]
    }

In above json, I want to validate that

  • if "relationship" = "My Self" then fullName must match the fullName in policyHolder
  • A field relationship must exist in traveller array, else json is invalid

I have tried to create a json schema with if-else, allOf, etc. but nothing works which can do these validations but not able to. Please help!!

Schema:

{
    "type": "object",
    "required": [
        "policyHolder",
        "traveller",
    ],
    "properties": {
        "policyHolder": {
            "$id": "#/properties/policyHolder",
            "type": "object",
            "required": [
                "fullName"
            ],
            "properties": {
                "fullName": {
                    "$id": "#/properties/policyHolder/properties/fullName",
                    "type": "string",
                }
            }
        },
        "traveller": {
            "$id": "#/properties/traveller",
            "type": "array",
            "minItems": 1,
            "items": {
                "$id": "#/properties/traveller/items",
                "type": "object",
                "properties": {
                    "fullName": {
                        "$ref": "#/properties/policyHolder/properties/fullName"
                    },
                    "relationship": {
                        "$id": "#/properties/traveller/items/properties/relationship",
                        "type": "string",
                    }
                },
                "required": [
                    "fullName",
                    "relationship"
                ],
                }
            }
        }
    }```

回答1:

It's your first requirement that you're going to have the most trouble with. JSON Schema doesn't support validation of data against data elsewhere in the instance. It's a highly discussed topic, but nothing has been adopted yet. I suggest you verify this with a little code.

For the second, I would suggest you extract some of your subschemas into definitions rather than trying to muck about with IDs. IDs are typically more beneficial if you're referencing them from other documents or if you use short (like single-word) IDs. Defining the ID as its location in the document is redundant; most processors will handle this automatically.

{
  "type": "object",
  "required": [
    "policyHolder",
    "traveller",
  ],
  "definitions": {
    "person": {
      "type": "object"
      "properties": {
        "fullName": {"type": "string"}
      },
      "required": ["fullName"]
    },
    "relationship": { "enum": [ ... ] }   // list possible relationships
  },
  "properties": {
    "policyHolder": { "$ref": "#/definitions/person" },
    "traveller": {
      "type": "array",
      "minItems": 1,
      "items": {
        "allOf": [
          { "$ref": "#/definitions/person" },
          {
            "properties": {
              "relationship": { "$ref": "#/definitions/relationship" }
            },
            "required": ["relationship"]
          }
        ]
      }
    }
  }
}

(I extracted the relationship into its own enum definition, but this is really optional. You can leave it inline, or even an unrestricted string if you don't have a defined set of relationships.)



回答2:

This can't currently be done with JSON Schema. All JSON Schema keywords can only operate on one value at a time. There's a proposal for adding a $data keyword that would enable doing this kind of validation, but I don't think it's likely to be adopted. $data would work like $ref except it references the JSON being validated rather than referencing the schema.

Here's what how you would solve your problem with $data.

{
  "type": "object",
  "properties": {
    "policyHolder": {
      "type": "object",
      "properties": {
        "fullName": { "type": "string" }
      }
    },
    "traveler": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "fullName": { "type": "string" },
          "relationship": { "type": "string" }
        },
        "if": {
          "properties": {
            "relationship": { "const": "My Self" }
          }
        },
        "then": {
          "properties": {
            "fullName": { "const": { "$data": "#/policyHolder/fullName" } }
          }
        }
      }
    }
  }
}

Without $data, you will have to do this validation in code or change your data structure so that it isn't necessary.