Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$dynamicAnchor inside propertyDependencies #615

Closed
gregsdennis opened this issue Nov 22, 2022 · 8 comments · Fixed by #617
Closed

$dynamicAnchor inside propertyDependencies #615

gregsdennis opened this issue Nov 22, 2022 · 8 comments · Fixed by #617

Comments

@gregsdennis
Copy link
Member

propertyDependencies is a conditional keyword. Similarly to if/then/else, you can repeat a $dynamicAnchor inside multiple branches, and $dynamicRef should be able to find the right one.

@gregsdennis gregsdennis transferred this issue from json-schema-org/json-schema-spec Nov 22, 2022
@handrews
Copy link
Contributor

This is a bit tricky b/c $dynamicAnchor has resource-level effects, not just schema object level. Repeating the same anchor in different keyword branches would have undefined behavior because there would be multiple valid resolution targets.

@gregsdennis
Copy link
Member Author

Repeating the same anchor in different keyword branches would have undefined behavior because there would be multiple valid resolution targets.

We have tests that do exactly this already with if/then/else:

{
"description": "multiple dynamic paths to the $dynamicRef keyword",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://test.json-schema.org/dynamic-ref-with-multiple-paths/main",
"$defs": {
"inner": {
"$id": "inner",
"$dynamicAnchor": "foo",
"title": "inner",
"additionalProperties": {
"$dynamicRef": "#foo"
}
}
},
"if": {
"propertyNames": {
"pattern": "^[a-m]"
}
},
"then": {
"title": "any type of node",
"$id": "anyLeafNode",
"$dynamicAnchor": "foo",
"$ref": "inner"
},
"else": {
"title": "integer node",
"$id": "integerNode",
"$dynamicAnchor": "foo",
"type": [ "object", "integer" ],
"$ref": "inner"
}
},
"tests": [
{
"description": "recurse to anyLeafNode - floats are allowed",
"data": { "alpha": 1.1 },
"valid": true
},
{
"description": "recurse to integerNode - floats are not allowed",
"data": { "november": 1.1 },
"valid": false
}
]
},

@gregsdennis
Copy link
Member Author

I'm working up a PR to illustrate a non-conflicting way that this could work.

@gregsdennis
Copy link
Member Author

gregsdennis commented Nov 22, 2022

I don't understand why you're saying it's undefined behavior.

Consider the schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://localhost:1234/draft2020-12/dynamicanchor-in-propertydependencies.json",
  "$defs": {
    "inner": {
      "$id": "inner",
      "$dynamicAnchor": "foo",
      "type": "object",
      "additionalProperties": {
        "$dynamicRef": "#foo"
      }
    }
  },
  "propertyDependencies": {
    "expectedTypes": {
      "strings": {
        "$id": "east",
        "$dynamicAnchor": "foo",
        "type": "string",
        "$ref": "inner"
      },
      "integers": {
        "$id": "west",
        "$dynamicAnchor": "foo",
        "type": "integer",
        "$ref": "inner"
      }
    }
  }
}

It's pretty clear to me that /propertyDependencies/expectedTypes/integers is only in dynamic scope when the instance has "expectedTypes": "integers". Only one subschema in the /propertyDependencies/expectedTypes branch could be active at once.

Yes, you could have that anchor spread across multiple properties, and that would be invalid/undefined.

{
  ...,
  "propertyDependencies": {
    "expectedTypes": {
      "strings": {
        "$id": "east",
        "$dynamicAnchor": "foo",
        "type": "string",
        "$ref": "inner"
      },
      "integers": {
        "$id": "west",
        "$dynamicAnchor": "foo",
        "type": "integer",
        "$ref": "inner"
      }
    },
    "anotherProp": {
      "valueA": {
        "$id": "north",
        "$dynamicAnchor": "foo",
        "type": "string",
        "$ref": "inner"
      },
      "valueB": {
        "$id": "south",
        "$dynamicAnchor": "foo",
        "type": "integer",
        "$ref": "inner"
      }
    }
  }
}

But the first schema doesn't do that.

@gregsdennis
Copy link
Member Author

I could also write

{
  "anyOf": [
    {
      "$dynamicAnchor": "foo",
      "type": "integer"
    },
    {
      "$dynamicAnchor": "foo",
      "type": "string"
    }
  ]
}

I would say that this is perfectly valid, but the behavior is undefined. It doesn't prevent us from having tests for the defined cases.

@handrews
Copy link
Contributor

handrews commented Nov 22, 2022

Because it's not the object-level dynamic scope, it's the resource-level dynamic scope. Read json-schema-org/json-schem-spec#1140 if you want the gory details.

@handrews
Copy link
Contributor

For that matter, since we haven't actually made the changes in issue 1140, this is even simpler: These schemas produce the same URI for different schema locations, and that results in undefined behavior (URI fragments are resource scoped, it doesn't matter whether the specific object is in the dynamic scope or not).

@handrews
Copy link
Contributor

Ah, my mistake- the allOf one is undefined, but you're correct, you have $id in the right places in the actual test, my apologies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants