Skip to content

patternProperties within complex schema structure #622

@mschulti

Description

@mschulti

I am using a schema which is structured into several sub-schema files.
It is working with v0.178.0 but it is based on relative paths within $ref.
As already explained here there is no support of "relative URIs" within $ref anymore.
Therefore I changed the the schema files accordingly to used proper paths within $ref properties.
That is working in principle with most of the JSON-Schema keywords, except with patternProperties in sub-schema files.

Following example shall demonstrate this issue:

namespace {
  auto main_schema = jsoncons::ojson::parse(R"({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "/schema.json",
    "type": "object",
    "properties": {
        "$schema": {
            "type": "string"
        },
        "version": {
            "description": "Version number of JSON content",
            "type": "string"
        },
        "features": {
            "description": "List of features supported by the device",
            "type": "object",
            "patternProperties": {
                "Firmware Update": {
                    "$ref": "/firmware_update.json#/$defs/firmware_update"
                }
            },
            "required": [
                "Firmware Update"
            ]
        }
    },
    "required": [
        "$schema",
        "version",
        "features"
    ],
    "additionalProperties": false
})");

  auto firmware_update_schema = jsoncons::ojson::parse(R"({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "/firmware_update.json",
    "type": "object",
    "$defs": {
        "firmware_update": {
            "allOf": [
                {
                    "description": "Firmware Update Feature description",
                    "properties": {
                        "feature_type": {
                            "const": "update"
                        },
                        "version": {
                            "enum": [
                                "V1",
                                "V2"
                            ]
                        }
                    }
                },
                {
                    "$ref": "/basic_types.json#/$defs/feature"
                }
            ]
        }
    }
})");

  auto basic_types_schema = jsoncons::ojson::parse(R"({
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "/basic_types.json",
    "type": "object",
    "$defs": {
        "parameter": {
            "type": "object",
            "description": "A configuration parameter",
            "properties": {
                "description": {
                    "type": "string"
                }
                "name": {
                    "description": "The name of the parameter in the config provider",
                    "type": "string"
                }
            },
            "required": [
                "name"
            ],
            "additionalProperties": false
        },
        "const_parameter": {
            "type": "object",
            "description": "A configuration parameter with a constant value",
            "properties": {
                "description": {
                    "type": "string"
                },
                "from": {
                    "const": "const"
                },
                "value": {
                    "description": "The constant value of the parameter",
                    "type": [
                        "string",
                        "number",
                        "boolean",
                        "array"
                    ]
                }
            },
            "required": [
                "from",
                "value"
            ]
        },
        "parameter_list": {
            "patternProperties": {
                "[a-zA-Z_]{1,}": {
                    "oneOf": [
                        {
                            "$ref": "#/$defs/parameter"
                        },
                        {
                            "$ref": "#/$defs/const_parameter"
                        }
                    ]
                }
            }
        },
        "feature": {
            "description": "A generic feature description",
            "properties": {
                "feature_type": {
                    "type": "string"
                },
                "version": {
                    "type": "string"
                },
                "description": {
                    "type": "string"
                },
                "config": {
                    "$ref": "#/$defs/parameter_list"
                }
            },
            "required": [
                "feature_type",
                "version"
            ]
        }
    }
})");

  std::string json_str = R"({
          "$schema": "/schema.json",
          "version": "v0.1",
          "features": {
              "Firmware Update": {
                  "feature_type": "update",
                  "version": "V2",
                  "config": {
                      "firmware_update": {
                          "from": "const",
                          "value": "xyz"
                      }
                  }
              }
          }
      })";
}

int main() {
  auto resolver = [](const jsoncons::uri& p_uri)
  {
    std::cout << "p_uri.path        : " << p_uri.path() << std::endl;

    if (my_file_path == "/basic_types.json")
    {
      return ::basic_types_schema;
    }
    if (my_file_path == "/firmware_update.json")
    {
      return ::firmware_update_schema;
    }
    std::cout << "Resolver cannot parse " << my_file_path << std::endl;
    return jsoncons::ojson::null();
  };

   auto validator = jsoncons::jsonschema::make_json_schema(::main_schema, resolver);
    jsoncons::json_decoder<jsoncons::ojson> decoder;
    validator.validate(jsoncons::ojson::parse(::json_str), decoder);

    const auto result = _decoder.get_result();
    std::cout << jsoncons::pretty_print(result);

   return 0;
}

The problem is caused by the patternProperties keyword within the parameter_list definition.
If I am exchange the parameter_list definition with

"parameter_list":
        {
            "properties": {
                "firmware_update": {
                    "oneOf": [
                        {
                            "$ref": "#/$defs/parameter"
                        },
                        {
                            "$ref": "#/$defs/const_parameter"
                        }
                    ]
                }
            }          
        }

then it works.

It is tested with - [x] Latest release 1.3.2

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions