-
-
Notifications
You must be signed in to change notification settings - Fork 313
Proposed Addition to JSON Schema: "Else-If" #1410
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
Comments
Thanks for the suggestion. There's definitely a gap here, but I think we'd probably need a different solution than the one suggested. In case it isn't known, the workaround you can use today to flatten your conditionals is to put them in an {
"allOf": [
{
"if": { ... },
"then": { ... }
},
{
"if": { ... },
"then": { ... }
},
...
]
} Each schema will pass if the So, while this workaround allows you to express what you need to express without excessive nesting, it would be nice to have a more ideal solution that doesn't require nesting. Back to the suggested The I'm not really sure how to reconcile these problems. I thought about it for a bit and here's the best I could come up with so far. I'll call this keyword " {
"conditional": [
{ ... }, // if
{ ... }, // then
{ ... }, // elsif
{ ... }, // then
{ ... }, // elsif
{ ... }, // then
{ ... } // else
]
} The biggest problem with this solution is that it's not very readable/maintainable. Two or three elements is fine, but once you start getting into the |
Does |
{
"anyOf": [
{ "if": false, "then": { "type": "object" } },
{ "if": false, "then": { "type": "string" } }
]
} The instance If you put an {
"anyOf": [
{ "if": false, "then": { "type": "object" }, "else": false },
{ "if": false, "then": { "type": "string" }, "else": false }
]
} Here, only objects and strings are allowed. |
Unfortunately, that doesn't work. The |
I am trying to think if there is some combination of nested
It might not end up extremely readable, but if it's already possible then maybe we can put some syntax sugar on top Unfortunately I'll have to come back to this tomorrow |
The goal here is to have either 0 or 1 (first applicable) condition in a list of conditions apply, making each condition mutually exclusive, but not requiring any of them. Flattening to an |
Seconding the usefulness of this feature. I agree that a shortcircuited version of allOf (which would mostly be used for conditionals or code generators) would be the most idiomatic representation of this. |
After more thought, the only way I can imagine to make My proposal would be something like this {
"firstOf": [
{"if": "PREDICATE_1", "then": "EXCLUSIVE_RESULT_1"},
{"if": "PREDICATE_2", "then": "EXCLUSIVE_RESULT_2"}
]
} In this example, if We should also define the base case: {
"firstOf": []
} Although I personally have no preference if this returns false, true, or doesn't parse. |
The
With this example, if |
I think I would suggest That said, this is a very niche application of an
|
Agreed.
Short circuiting alone (whether
This appears to be the same as the original proposal for |
Now I'm worried that I misunderstood the feature request. Isn't this statement also true about else-if? If the issue is naming, I think I prefer
Agreed, I chose
Maybe it would evaluate them in order until one was true and return it directly, or maybe it's not a valid schema? I'm not sure
Sorry, I don't know and can't answer this one
One substantial difference is that it does not require
I think you really highlighted the core of the problem. We're trying to fit an imperative tool into a functional box. Lisp has a (cond (test-expression1 then-expression1)
(test-expression2 then-expression2)
(t else-expression2)) (source) On that note, would using an array of arrays of schemas be any better? {
"conditional": [
[{ ... }, { ... }], // if, then
[{ ... }, { ... }], // elsif, then
[{ ... }, { ... }], // elsif then
[{ ... }] // else
]
} If the |
This entire issue/discussion is one of the hesitations I had about introducing
Yes, an implementation has to process
|
For We have no basis for a keyword that applies subschemas sequentially, especially one that bails out part-way through. You may ask, "What about |
Looking around to see what others have done in other schema formats. Here is an example of XSD-based modeling to express these concepts:
Following a similar pattern could bring us back to the example in the first post:
The schema for
|
This is still subverting some core assumptions about JSON schema:
Even |
I thought about pairing them as tuples as well, but decided against it in the moment. I think there are times when it's best expressed as tuples and times when it's not. In a simple
While it's true that there aren't currently any keywords that work that way, I don't see any reason why a keyword like that would be a problem. It doesn't introduce the need for any new capabilities to the JSON Schema architecture. |
It might be relevant to point out the {
"propertyDependencies": {
"foo": {
"a": { ... },
"b": { ... },
"c": { ... },
...
}
}
} This keyword defines a schema to apply if a property has a given value. In this case, if the value of property |
This
is equivalent to this
which is pure boolean logic and fully compatible with
p.s. if it simplifies implementation, this is also equivalent:
this way, some "union" exists and can be dropped into
|
We've avoided structured keywords like this in the past. That's the main reason Although a structured keyword would be a unique addition to JSON Schema, that's not my concern with this proposal. My concern is that because Interestingly, since names are the problem, if we convert the structured |
I think using references might help this a little so that you're not copying constraints everywhere. {
"$defs": {
"A": { ... },
"B": { ... },
"C": { ... },
},
"allOf": [
{
"if": { "$ref": "#/$defs/A" },
"then": { ... } // W
},
{
"if": {
"allOf": [
{ "not": { "$ref": "#/$defs/A" } },
{ "$ref": "#/$defs/B" }
]
},
"then": { ... } // X
},
{
"if": {
"allOf": [
{ "not": { "$ref": "#/$defs/A" } },
{ "not": { "$ref": "#/$defs/B" } },
{ "$ref": "#/$defs/C" }
]
},
"then": { ... } // Y
},
{
"if": {
"allOf": [
{ "not": { "$ref": "#/$defs/A" } },
{ "not": { "$ref": "#/$defs/B" } },
{ "not": { "$ref": "#/$defs/C" } }
]
},
"then": { ... } // Z
},
]
} While this makes it a bit easier to read, unless your implementation is doing a really good job of caching results, you still have multiple evaluations for each definition as it still has to evaluate all of the options. |
When defining mutually exclusive if-statements (
if
,else-if
,else-if
, etc.), JSON Schema currently requires nesting with theelse
statements. For shallow cases, this works fairly well. For example:However, for highly complex use-case where there are hundreds of mutually exclusive conditions, this results in nested
if-then-else
statements more than 100 deep. This complicates use-cases where clients are using JSON Schema for non-validation use-cases (e.g. mapping rules to their own data models from our JSON Schemas) as well as causes issues with commonly uses open-source validator implementations (~100 is a commonly used "safety" limit in some open source libraries to prevent infinite recursion).Since conditional logic (
if-then-else
) is defined by standard JSON Schema vocabularies, it would be ideal if we could come up with a way to flattenelse-if
behaviors in the standard JSON Schema vocabularies (rather than using custom vocabularies that most open-source implementations would not be able to handle by default). Something like this:Thanks for your consideration.
The text was updated successfully, but these errors were encountered: