Search code examples
pythonjsonjsonschema

How can I avoid breaking $ref without removing $id in my JSON Schema?


The presence of the $id field in the following JSON Schema causes a jsonschema.exceptions._WrappedReferencingError when I try to call jsonschema.validate on it using the jsonref library in python.

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "definitions": {
        "first_definition": {
          "value": "this is definitions/first_definition"
        },
        "second_definition": {
          "$id": "any_value",
          "properties": {
            "sections": {
              "$ref": "#/definitions/first_definition"
            }
          }
        }
      },
      "$ref": "#/definitions/second_definition"
    }

Apparently having an $id changes the scope that $ref can see? The $ref that fails is the '/definitions/first_definition' ref, and the WrappedReferencingError holds a referencing.exceptions.PointerToNowhere'. I can remove the $id to get rid of the error, but that seems like the wrong solution. Is there a way to still have an $id, but without breaking the $ref?


Solution

  • Why do you think you need the $id? I'm just curious if you think there is use case you believe it solves.

    The $id acts as a scoping mechanism. It's an identifier for a schema. Essentially, it's creating a new "file" of just that schema. so the use of #/definitions/first_definition means it's looking for the first_definition in the any_value schema root.

    think of it like this..

    root schema

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "definitions": {
        "first_definition": {
          "value": "this is definitions/first_definition"
        },
        "second_definition": {
          "$id": "any_value",
          "properties": {
            "sections": {
              "$ref": "#/definitions/first_definition"
            }
          }
        }
      },
      "$ref": "#/definitions/second_definition"
    }
    

    any_value schema.

    first_definition is not defined in this schema, so it doesn't resolve to anything, thus throws an error

    {
      "$id": "any_value",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object",
      "properties": {
        "sections": {
          "$ref": "#/definitions/first_definition"
        }
      },
      "definitions": {}
    }
    

    When you remove the $id, it changes the scope of the #/definitions/... to the root of the original schema, where it finds the definition for first_definintion defined.

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "definitions": {
        "first_definition": {
          "value": "this is definitions/first_definition"
        },
        "second_definition": {
          "properties": {
            "sections": {
              "$ref": "#/definitions/first_definition"
            }
          }
        }
      },
      "$ref": "#/definitions/second_definition"
    }
    
    

    If you really have your heart set on using $id, you need to give the root schema an id to reference. Then you can resolve the reference.

    {
      "$id": "root_schema",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "definitions": {
        "first_definition": {
          "value": "this is definitions/first_definition"
        },
        "second_definition": {
          "$id": "any_value",
          "properties": {
            "sections": {
              "$ref": "root_schema#/definitions/first_definition"
            }
          }
        }
      },
      "$ref": "#/definitions/second_definition"
    }
    
    

    An alternative to the id reference is to use relative or absolute file path references.

    assume the file name is root.schema.json. This will resolve to the file path and the root of the schema becomes available with the # which allows you to navigate through the file.

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "definitions": {
        "first_definition": {
          "value": "this is definitions/first_definition"
        },
        "second_definition": {
          "$id": "any_value",
          "properties": {
            "sections": {
              "$ref": "./root.schema.json#/definitions/first_definition"
            }
          }
        }
      },
      "$ref": "#/definitions/second_definition"
    }