python-jsonschema / jsonschema

An implementation of the JSON Schema specification for Python
https://python-jsonschema.readthedocs.io
MIT License
4.58k stars 578 forks source link

'Tuple' arrays not handled correctly #1202

Closed vdweij closed 9 months ago

vdweij commented 9 months ago

In a json schema you could define a 'tuple' array (https://json-schema.org/understanding-json-schema/reference/array#tupleValidation) like:

{
    "title": "Complex-Array-Schema",
    "type": "object",
    "properties": {
        "tupleArrayValue": {
            "type": "array",
            "items": [
                { "type": "string" },
                { "type": "number" },
                { "type": "boolean" },
                { "type": "object", "properties": { "name": { "type": "string" } } },
                { "type": "array", "items": { "type": "number" } }
            ],
            "description": "A tuple array type",
            "additionalItems" : false
        }
    }
}

meaning the corresponding json could look like this:

{
    "tupleArrayValue": [
      "et proident sunt mollit do",
      -66513280.47532514,
      true,
      {
        "name": "laboris"
      },
      [
        -78998381.61689971,
        53630079.73981416
      ]
    ]
  }

The order of the types is important and no additional values are allowed because additionalItems is set to false.

Above json should conform to the specified schema. However I get a SchemaError when I try to validate this using the lib jsonschema. It looks like the schema itself is considered invalid.

Using another lib (https://github.com/ajv-validator/ajv) via https://extendsclass.com/json-schema-validator.html shows it is valid. Also https://www.jsonschemavalidator.net/ (https://github.com/JamesNK/Newtonsoft.Json) shows it is valid.

Below a simple reproduction snippet

import jsonschema

#A sample schema, like what we'd get from json.load()
schema = {
    "type": "object",
    "properties": {
        "complexArrayValue": {
            "type": "array",
            "items": [
                { "type": "string" },
                { "type": "number" }
            ]
        }
    }
}

instance = {
    "complexArrayValue": [
        "et proident sunt mollit do",
        -66513280.47532514
    ]
}

# If no exception is raised by validate(), the instance is valid.
jsonschema.validate((instance=instance, schema=schema)

Results in:

jsonschema.exceptions.SchemaError: [{'type': 'string'}, {'type': 'number'}] is not of type 'object', 'boolean'

Failed validating 'type' in metaschema['allOf'][1]['properties']['properties']['additionalProperties']['$dynamicRef']['allOf'][1]['properties']['items']['$dynamicRef']['allOf'][0]:
    {'$defs': {'anchorString': {'pattern': '^[A-Za-z_][-A-Za-z0-9._]*$',
                                'type': 'string'},
               'uriReferenceString': {'format': 'uri-reference',
                                      'type': 'string'},
               'uriString': {'format': 'uri', 'type': 'string'}},
     '$dynamicAnchor': 'meta',
     '$id': 'https://json-schema.org/draft/2020-12/meta/core',
     '$schema': 'https://json-schema.org/draft/2020-12/schema',
     '$vocabulary': {'https://json-schema.org/draft/2020-12/vocab/core': True},
     'properties': {'$anchor': {'$ref': '#/$defs/anchorString'},
                    '$comment': {'type': 'string'},
                    '$defs': {'additionalProperties': {'$dynamicRef': '#meta'},
                              'type': 'object'},
                    '$dynamicAnchor': {'$ref': '#/$defs/anchorString'},
                    '$dynamicRef': {'$ref': '#/$defs/uriReferenceString'},
                    '$id': {'$comment': 'Non-empty fragments not allowed.',
                            '$ref': '#/$defs/uriReferenceString',
                            'pattern': '^[^#]*#?$'},
                    '$ref': {'$ref': '#/$defs/uriReferenceString'},
                    '$schema': {'$ref': '#/$defs/uriString'},
                    '$vocabulary': {'additionalProperties': {'type': 'boolean'},
                                    'propertyNames': {'$ref': '#/$defs/uriString'},
                                    'type': 'object'}},
     'title': 'Core vocabulary meta-schema',
     'type': ['object', 'boolean']}

On schema['properties']['complexArrayValue']['items']:
    [{'type': 'string'}, {'type': 'number'}]
Julian commented 9 months ago

As the link you yourself shared says, items with an array is called prefixItems in newer JSON Schema versions, and your schema is indeed therefore invalid in those versions.

Using another lib (https://github.com/ajv-validator/ajv) via https://extendsclass.com/json-schema-validator.html shows it is valid. Also https://www.jsonschemavalidator.net/ (https://github.com/JamesNK/Newtonsoft.Json) shows it is valid.

Both of these libraries are notoriously buggy by the way.

vdweij commented 9 months ago

Thnx... I did find a related issue https://github.com/python-jsonschema/jsonschema/issues/1015 I guess I need to specify the $schema version in this case.

Julian commented 9 months ago

If you intend your schema to be for an older version, yes.