java-json-tools / json-schema-validator

A JSON Schema validation implementation in pure Java, which aims for correctness and performance, in that order
http://json-schema-validator.herokuapp.com/
Other
1.63k stars 399 forks source link

Fails to validate `polymorphic` schema #282

Open anatoliy-balakirev opened 5 years ago

anatoliy-balakirev commented 5 years ago

Following json:

{
  "name": "Some name",
  "my_label": {
    "type": "multi",
    "format": "Your subscription will automatically renew on %1$@.",
    "value": [
      {
        "type": "string",
        "value": "Some date"
      }
    ]
  },
  "id": "some id"
}

Is reported as invalid when validated against following schema:

{
  "required": [
    "id",
    "name",
    "my_label"
  ],
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "my_label": {
      "oneOf": [
        {
          "required": [
            "type",
            "value"
          ],
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "enum": [
                "string"
              ]
            },
            "value": {
              "type": "string"
            }
          },
          "description": "Plain text string. Exists mainly to provide polymorphic behavior for other formats while allowing to use plain string."
        },
        {
          "required": [
            "format",
            "type",
            "value"
          ],
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "enum": [
                "multi"
              ]
            },
            "value": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/FormattedValue"
              }
            },
            "format": {
              "type": "string"
            }
          },
          "description": "Composes other formatted values types to form more complex formatted string."
        }
      ]
    }
  },
  "$schema": "https://openapis.org/specification/versions/2.0#",
  "additionalProperties": false,
  "components": {
    "schemas": {
      "FormattedValue": {
        "oneOf": [
          {
            "required": [
              "type",
              "value"
            ],
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "string"
                ]
              },
              "value": {
                "type": "string"
              }
            },
            "description": "Plain text string. Exists mainly to provide polymorphic behavior for other formats while allowing to use plain string."
          },
          {
            "required": [
              "format",
              "type",
              "value"
            ],
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "multi"
                ]
              },
              "value": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/FormattedValue"
                }
              },
              "format": {
                "type": "string"
              }
            },
            "description": "Composes other formatted values types to form more complex formatted string."
          }
        ],
        "additionalProperties": false
      },
      "FormattedValueString": {
        "required": [
          "type",
          "value"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "string"
            ]
          },
          "value": {
            "type": "string"
          }
        },
        "description": "Plain text string. Exists mainly to provide polymorphic behavior for other formats while allowing to use plain string.",
        "additionalProperties": false
      },
      "FormattedValueMulti": {
        "required": [
          "format",
          "type",
          "value"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "multi"
            ]
          },
          "value": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FormattedValue"
            }
          },
          "format": {
            "type": "string"
          }
        },
        "description": "Composes other formatted values types to form more complex formatted string.",
        "additionalProperties": false
      }
    }
  }
}

Here is a validation output from http://json-schema-validator.herokuapp.com/ :

[ {
  "level" : "warning",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : ""
  },
  "domain" : "syntax",
  "message" : "the following keywords are unknown and will be ignored: [components]",
  "ignored" : [ "components" ]
}, {
  "level" : "error",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/my_label"
  },
  "instance" : {
    "pointer" : "/my_label"
  },
  "domain" : "validation",
  "keyword" : "oneOf",
  "message" : "instance failed to match exactly one schema (matched 0 out of 2)",
  "matched" : 0,
  "nrSchemas" : 2,
  "reports" : {
    "/properties/my_label/oneOf/0" : [ {
      "level" : "error",
      "schema" : {
        "loadingURI" : "#",
        "pointer" : "/properties/my_label/oneOf/0/properties/type"
      },
      "instance" : {
        "pointer" : "/my_label/type"
      },
      "domain" : "validation",
      "keyword" : "enum",
      "message" : "instance value (\"multi\") not found in enum (possible values: [\"string\"])",
      "value" : "multi",
      "enum" : [ "string" ]
    }, {
      "level" : "error",
      "schema" : {
        "loadingURI" : "#",
        "pointer" : "/properties/my_label/oneOf/0/properties/value"
      },
      "instance" : {
        "pointer" : "/my_label/value"
      },
      "domain" : "validation",
      "keyword" : "type",
      "message" : "instance type (array) does not match any allowed primitive type (allowed: [\"string\"])",
      "found" : "array",
      "expected" : [ "string" ]
    } ],
    "/properties/my_label/oneOf/1" : [ {
      "level" : "error",
      "schema" : {
        "loadingURI" : "#",
        "pointer" : "/components/schemas/FormattedValue"
      },
      "instance" : {
        "pointer" : "/my_label/value/0"
      },
      "domain" : "validation",
      "keyword" : "additionalProperties",
      "message" : "object instance has properties which are not allowed by the schema: [\"type\",\"value\"]",
      "unwanted" : [ "type", "value" ]
    } ]
  }
} ]

Why is it the case? Json seems to be valid... If I adjust schema to use "additionalProperties": true - then validation passes properly. Also in that case those fields are actually validated: when I change my json to have this, for example: "type": "string2" (instead of "type": "string") - I get proper validation errors. I do not want to set that additionalProperties flag to true in my real application. We have to reject additional properties there.

anatoliy-balakirev commented 5 years ago

If someone is interested in what is actually the use case for that weird schema: we are getting it from the front end, where they want to have some polymorphic object, which they can render in some generic way. Original schema is much bigger, I've extracted only part, which is causing an issue. This schema is generated based on openapi spec, which we use as a contract between front end and back end. On our side we are basically validating generated responses against that schema.