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

Support non-hierarchical dependencies #20

Open
yhack opened this issue May 17, 2018 · 8 comments
Open

Support non-hierarchical dependencies #20

yhack opened this issue May 17, 2018 · 8 comments

Comments

@yhack
Copy link

yhack commented May 17, 2018

I'm very interested in using JSON Schema to model documents that have non-hierarchical intradependencies.

The dependency specification includes this statement:

If the dependency value is a subschema, and the dependency key is a property in the instance, the entire instance must validate against the dependency value.

In other words, if this object has the dependency key, then this object must validate against the subschema. The crucial functionality missing is the ability to conditionally apply a subschema to a different part of the document.

Notes:

  • Removing dependencies? json-schema-spec#442 Explains how dependencies can be written using if-then-else. While useful, it doesn't supply the functionality mentioned above.
  • The $ref keyword validates the current instance against the referenced schema. It does not validate a different part of the document against a schema.
  • https://github.com/json-schema-org/json-schema-spec/issues/141 is about resolving JSON Pointers defined in JSON documents that reference JSON documents. This proposal is about resolving JSON Pointers defined in JSON Schema that reference the JSON document being validated.
  • Relative JSON Pointer could be used to implement document references.
  • The $data proposal ($data json-schema-spec#51) does use Relative JSON Pointers to reference the document being validated. It seems to ingest the referenced value into the schema. I don't see how this could be used to apply a subschema to the referenced value.

The ability to model intradocument dependencies would be useful both for UI generation and consistency checking of JSON documents with non-hierarchical relationships. Given JSON Schema's existing tooling, extending it to support this use case should be relatively straightforward.

@handrews
Copy link
Contributor

@yhack can you give some examples of this sort of dependency? My feeling is that if is more flexible than it might initially appear. As a simple example, if you want property "x" to be either a string on an integer based on the value of boolean property "y", then you would write something like:

{
  "type": "object",
  "required": ["x", "y"],
  "properties": {
    "y": {"type": "boolean"}
  },
  "if": {
    "properties": {
      "y": {"const": true}
    }
  },
  "then": {
    "properties": {
      "x": {"type": "string"}
    }
  },
  "else": {
    "properties": {
      "x": {"type": "integer"}
    }
  }
}

You may also find @awwright's data relationships proposal interesting: json-schema-org/json-schema-spec#549.

@yhack
Copy link
Author

yhack commented May 18, 2018

@handrews I'm working on getting you an example I can share. In the meantime, I'll build on your example. The use case occurs when interdependent data is nested in different branches of the JSON tree. Referring to your example, suppose "x" and "y" were each objects with properties "a" and "b" respectively. Suppose you want to assert something about "a" based on the value of "b". Technically you could accomplish this with an if then statement in the root object, but this would quickly get messy when you have many relations to express and a deeply nested structure. In other words, I'm proposing that an if statement in the "b" schema definition can then assert a schema on "a".

json-schema-org/json-schema-spec#549 is interesting, but both that and json-schema-org/json-schema-spec#51 are about importing data into the schema rather than applying schema to an arbitrary part of the JSON document.

@yhack
Copy link
Author

yhack commented Feb 13, 2019

Here's an example. Suppose we have the following JSON document describing a birthday party:

{
    "guests": ["John", "Sam"],
    "dessert": "cake"
}

We might validate guests and desserts as follows:

{
  "properties": {
    "guests": {
      "type": "array",
      "items": {
        "enum": ["John", "Sam", "Lucy"]
      }
    },
    "dessert": {
      "type": "string",
      "enum": ["cake", "ice cream", "brownies", "cookies"]
    }
  }
}

Now suppose our guests are rather picky. As a caring host we want to provide a dessert that every guest likes. Dessert likes are provided by the following table:

            John    Sam    Lucy
cake        X       X
ice cream   X       X      X
brownies    X     
cookies             X      X

As you can see, which desserts are valid depends on which guests attend. Validating this type of internal consistency using JSON Schema is currently not possible in a general and elegant manner.* I'd like to propose a simple extension to support this.

Proposal: $focus node

As presently designed, which part of a JSON document that a JSON sub schema validates is based on the position of the sub schema relative to its parent schema (and as modified by $ref). Let's call the part of the JSON document that a particular sub-schema validates the focus node. Now, if we introduce a key word, lets call it $focus, to change the focus node using a (Relative) JSON Pointer, we can perform the validation needed for our birthday party document as follows:

{
  "properties": {
    "guests": {
      "type": "array",
      "items": {
        "enum": ["John", "Sam", "Lucy"]
      },
      "allOf": [
        {
          "if": {
            "contains": {
              "const": "John"
            }
          },
          "then": {
            "$focus": "/dessert",
            "enum": ["cake", "ice cream", "brownies"]
          }
        },
        {
          "if": {
            "contains": {
              "const": "Sam"
            }
          },
          "then": {
            "$focus": "/dessert",
            "enum": ["cake", "ice cream", "cookies"]
          }
        },
        {
          "if": {
            "contains": {
              "const": "Lucy"
            }
          },
          "then": {
            "$focus": "/dessert",
            "enum": ["ice cream", "cookies"]
          }
        }
      ]
    },
    "dessert": {
      "type": "string",
      "enum": ["cake", "ice cream", "brownies", "cookies"]
    }
  }
}          

Given this schema, our JSON birthday party document is valid because both guests John and Sam like cake. If Lucy were a guest, however, it would fail validation because cake is not one of her likes.

*The above validation might be accomplished with If Then statements at the top level but this can become terribly messy with deeply nested non-local constraints as per my previous post.

Keyword Comparison

no keyword: The current schema instance being processed applies to the current JSON document instance being processed, or focus node.
$ref: references JSON Schema. $ref applies the referenced schema to the focus node.
$data: references a point in the JSON document relative to the focus node. The referenced value is incorporated into the schema at the point of reference.
$focus: changes the focus node to a point in the JSON document relative to the (former) focus node. The current schema instance being processed applies to the new focus node.

Does this sound reasonable?

@awwright
Copy link
Member

I'd like to see an example of how this is useful for structural validation. JSON Schema generally does not encode valid data ranges; that is to say, if the guest list changes, the schema should still remain the same.
So first question is, do you have an example that isn't validating the consistency of the data?

@yhack
Copy link
Author

yhack commented Feb 19, 2019

do you have an example that isn't validating the consistency of the data?

You hit the nail on the head. The point of this proposal is to validate data consistency.

I'd like to see an example of how this is useful for structural validation.

With $focus it's possible to write a schema that requires the structure to change based on JSON data. I'm not sure what that would be useful for but trivially John could require a dessert that is not simply a string but a complex object of name and ingredients. Is that what you are asking for?

@handrews
Copy link
Contributor

@yhack With draft 2019-09 out (and hopefully soon having more than one implementation- although we're still working on the test suite), I'm encouraging folks with complex special-case keyword requests to implement an extension vocabulary for them. This is for several reasons:

  • The long tail of specialized keyword requests is very long, and we will never reach standardization trying to accommodate them
  • We want to encourage implementations to support extension through the new $vocabulary mechanism, which is most likely to happen if people design some vocabularies that are useful
  • We want to get feedback on the vocabulary concept and implementation

But mostly, it's that there are tons of requests for really complex things that not very many people ask for. We have to draw the line somewhere, and at this point there's a really, really high bar for keywords going into the Core and Validations specifications. This is exactly why we designed the vocabulary feature.

@handrews
Copy link
Contributor

As noted above, this is probably more of an extension vocabulary thing. Without the $ prefix as that is reserved to core. This is also related to json-schema-org/json-schema-spec#855.

@handrews handrews transferred this issue from json-schema-org/json-schema-spec Feb 28, 2020
@yashwanthkalva
Copy link

Without this feature, how do you write a validation, if you want it fail if two integer type properties are same, the values to these 2 properties will be either provided as input or has defaults. Please let me know.

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

No branches or pull requests

4 participants