Search code examples
geojsonjsonschemajson-schema-validator

How do I add additional constraints to a property in a referenced schema in JSON Schema?


I am trying to write a JSON Schema to impose additional required properties in a referenced schema. I have looked here and here, but check-jsonschema tells me that I need the properties in the top level, not in the nested properties object. How do I add a constraint on a nested object property?

Attempted schema:

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "$id": "https://example.com/sample.schema.json",
  "type": "object",
  "properties": {
    "boundaries": {
      "description": "Boundaries",
      "allOf": [
        { "$ref": "http://geojson.org/schema/Feature.json" },
        {
          "geometry": {
            "oneOf": [
              { "$ref": "http://geojson.org/schema/MultiPolygon.json" },
              { "$ref": "http://geojson.org/schema/Polygon.json" }
            ],
            "additionalProperties": false
          }
        },
        { 
          "type": "object",
          "required" : ["name"]
        }
      ]
    }
  }
}

I'd like the following to succeed, but the above validator asks me to put name at the same level as type, properties, and geometry.

JSON instance:

{
  "boundaries": {
    "type": "Feature",
    "properties": {
      "name": "Bounded Place",
    },
    "geometry": {
      "type": "Polygon",
      "coordinates": [
        [
          [ -100.0, 40.0 ],
          [ -100.0, 45.0 ],
          [ -110.0, 45.0 ],
          [ -110.0, 40.0 ]
        ]
      ]
    }
  }
}

Solution

  • You have a couple of issues with the attempted schema

    • the $schema declaration for draft-07 must use http rather than https. This is not considered to be a network reachable uri, but rather an identifier for the meta-schema. src: draft-07 spec
    • the second allOf subschema doesn't define the property geometry correctly. This schema is completed unconstrained and will allow any data to pass. You can only constrain properties with the properties keyword defined. Did you want to add additional validation on the geometry keyword that the referenced schema doesn't validate?
    • additionalProperties: false in the second subschema will also make this entire schema fail any instance because ALL subschemas of an allOf must be valid. There are two reasons why this will fail. Firstly, when additionalProperties:false is defined with any composition keyword (allOf, oneOf, anyOf), and no properties keyword is defined, the schema will never pass any data instance because the additional properties are constrained at the root schema and it does not "reach into" the oneOf array of subschemas, so it's not aware of any defined properties at the root, thus it fails any attempt to validate any schema. Secondly, When you constrain one of the subschemas to no additional properties inside an allOf, if the properties from the other subschemas are not defined in the schema with additionalProperties: false, all of the schemas will fail.
    • you have required: ["name"] at the wrong nested level because the Feature.json schema has a property called properties (which can be confusing) and this is where you have defined name in your instance.

    From my understanding, you want to require name in your data instance. To do this, you need to define the nested structure in an allOf subschema and then require that property. Because the property properties is defined in the geoJSON schema and it appears that is where you want this requirement, this is how you would achieve that.

    JSON Schema definition

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "$id": "https://example.com/sample.schema.json",
      "type": "object",
      "properties": {
        "boundaries": {
          "description": "Boundaries",
          "allOf": [
            {
              "type": "object",
              "properties": {
                "properties": {
                  "required": [
                    "name"
                  ]
                }
              }
            },
            {
              "$ref": "https://geojson.org/schema/Feature.json"
            }
          ]
        }
      }
    }
    

    data instance

    {
      "boundaries": {
        "type": "Feature",
        "properties": {
          "name": "Bounded Place"
        },
        "geometry": {
          "type": "Polygon",
          "coordinates": [
            [
              [ -100.0, 40.0 ],
              [ -100.0, 45.0 ],
              [ -110.0, 45.0 ],
              [ -110.0, 40.0 ]
            ]
          ]
        }
      }
    }