how to set the type of a schema object based on th

2019-08-01 20:40发布

问题:

I have an object (from a 3rd party, so I can't change it) that have a property named "key", and another property called "value" that is optional, and it's type depends on the value of the "key" property.

For instance:
If the key is "comment", the type of value {"Text":"commentValue"}.
If the key is "offset", the type of value is {"seconds":int}.
If the key is "weather", the type of value is {"value": Enum["sun", "clouds", "rain"...]}

Moreover, some of the keys do not have the value property, so the schema should forbid it from appearing with these keys. one of these keys is "standby" (as you can see in my current attempt below)

I've tried manipulating the code samples from this SO answer, but couldn't make it work.

I'm currently attempting to validate output json against my schema attempts using Newtonsoft's JSON Schema Validator - but I can't seem to get the "value" property defined correctly.

This is my code so far:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "TestOptionalObject",
    "type": "object",
    "additionalProperties": false,
    "required": [
        "test"
    ],
    "properties": {
        "test": {
            "$ref": "#/definitions/test"
        }
    },
    "definitions": {
        "test": {
            "type": "object",
            "additionalProperties": false,
            "required": [
                "key",
            ],
            "properties": {
                "key": {
                    "type": "string",
                    "enum": ["standby", "comment", "offset"]
                },
                "value" : {
                    "if": {
                        "properties": {
                          "key": {"enum": ["comment"]}
                        }
                    },
                    "then": { 
                        "$ref": "#/definitions/commentValue"
                    },
                    "if": {
                        "properties": {
                          "key": {"enum": ["offset"]}
                        }
                    },
                    "then": { 
                        "$ref": "#/definitions/offsetValue"
                    }
                }
            }
        },
        "commentValue" : {
            "type": "object",
            "additionalProperties": false,
            "required": [
                "text",
            ],
            "properties": {
                "text" : {"type" : "string"}
            }
        },
        "offsetValue" : {
            "type": "object",
            "additionalProperties": false,
            "required": [
                "seconds",
            ],
            "properties": {
                "seconds" : {
                    "type": "integer",
                    "format": "int32"
                }
            }
        }
    }
}

And this is the error messages I get:

JSON does not match schema from 'then'. Schema path: #/definitions/offsetValue/then

Property 'text' has not been defined and the schema does not allow additional properties. Schema path: #/definitions/offsetValue/additionalProperties

Required properties are missing from object: seconds. Schema path: #/definitions/offsetValue/required

Json examples to validate:

Should fail:

{
  "test": {
    "key": "comment",
      "value": {"seconds":12}
  }
}


{
  "test": {
    "key": "standby",
     "value": {"asdf":12}
  }
}

Should pass:

{
  "test": {
    "key": "comment",
     "value": {"text":"comment text"}
  }
}


{
  "test": {
    "key": "offset",
     "value": {"seconds":12}
  }
}

回答1:

I have changed your JSON Schema so it does what you expect, apart form key of standby as you didn't include that in your schema, and you should be able to replicate the pattern I've created to add new keys as required.

The major issue you had was a false assumption about where to place if/then/else keywords. They are applicator keywords, and so must be applied to the object which you are checking the condition of, and not a properties key value. Because you were using if/then/else in the object which was a value of value, you were applying if/then/else to the value of value rather than test.

You needed your if to apply to test to get the correct scope for checking the key property value.

Here is the resulting fixed schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "TestOptionalObject",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "test"
  ],
  "properties": {
    "test": {
      "$ref": "#/definitions/test"
    }
  },
  "definitions": {
    "test": {
      "type": "object",
      "required": [
        "key"
      ],
      "properties": {
        "key": {
          "type": "string",
          "enum": [
            "standby",
            "comment",
            "offset"
          ]
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "key": {
                "const": "comment"
              }
            }
          },
          "then": {
            "properties": {
              "value": {
                "$ref": "#/definitions/commentValue"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "key": {
                "const": "offset"
              }
            }
          },
          "then": {
            "properties": {
              "value": {
                "$ref": "#/definitions/offsetValue"
              }
            }
          }
        }
      ]
    },
    "commentValue": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "text"
      ],
      "properties": {
        "text": {
          "type": "string"
        }
      }
    },
    "offsetValue": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "seconds"
      ],
      "properties": {
        "seconds": {
          "type": "integer",
          "format": "int32"
        }
      }
    }
  }
}

If you want any more help, please feel free to join the JSON Schema slack using the discussion link on the http://json-schema.org site.



标签: jsonschema