0

I have a JSON Schema using draft 2020-12 and I am trying to use an if-else subschema to check that a particular property exists based on the value of another property. Below is the if statement I am currently using. There are more properties but I have have omitted them for the sake of brevity. They are identical except the type of the property in the then statement is different. They are all wrapped in an allOf array:

{
    "AValue": {
        "allOf": [
            {
                "if": {
                    "$ref": "#/$defs/ValueItem/properties/dt",
                    "const": "type1"
                },
                "then": {
                    "properties": {
                        "string": { "type": "string" }
                    },
                    "required": ["string"]
                }
            }
        ]
    }
}

The #/$defs/ValueItem/properties/dt being referred to is here:

{
    "ValueItem": {
        "properties": {
            "value": {
                "$ref": "#/$defs/AValue"
            },
            "dt": {
                "$ref": "#/$defs/DT"
            }
        },
        "additionalProperties": false
    }
}

#/$defs/DT is here:

{
    "DT": {
        "type": "string",
        "enum": [
            "type1",
            "type2",
            "type3",
            "type4"
        ]
    }
}

I expected that when dt is encountered in a JSON instance document, the validator will check if the value of dt is type1 and then validate that an additional property called string is also present and is of type string. However, what actually happens is the validator complains that "Property 'string' has not been defined and the schema does not allow additional properties".

I assume that this is because the condition in the if statement evaluates to false so the subschema is never applied. However, I am unsure why that would be as I followed the example here when creating the if-then-else block. The only thing I can think of that is different is the use of $ref which I have in my schema but it is not in the example.

I found this answer which makes me think that it is possible to use $ref in an if statement but is it possible to use a ref that points to another ref or am I thinking about it incorrectly?

I have also tried removing the $ref from the if statement like below but it still doesn't work. Is it because of the $ref in the properties?

{
    "AValue": {
        "properties": {
            "dt": {
                "$ref": "#/$defs/DT"
            }
        },
        "required": [
            "dt"
        ],
        "allOf": [
            {
                "if": {
                    "properties": {
                        "dt": {
                            "const": "type1"
                        }
                    }
                },
                "then": {
                    "properties": {
                        "string": {
                            "type": "string"
                        }
                    }
                }
            }
        ]
    }
}
word4q
  • 36
  • 10

1 Answers1

1

The problem is not cascading the $ref keywords. The const keyword at the if statement is not applied to the target of the $ref, but to the current location in the JSON input data. In this case to "AValue". To check if the property "dt" is of value "type1" at this point, you would need an if like this (simple solution with no $ref):

"if": {
  "properties": {
    "dt": {
      "const": "type1"
    }
  },
  "required": [ "dt" ]
}

Edit: Showing complete JSON Schema and error in JSONBuddy with $ref:

enter image description here

Clemens
  • 1,744
  • 11
  • 20
  • I have tried to take your answer onboard and adjusted my schema but it still doesn't work (same error as before). I have edited the question to show my updated schema, is there something that I'm not understanding? – word4q Feb 06 '23 at 11:12