JSON Schema property dependent on value of previous property











up vote
1
down vote

favorite












I'd like to be able to write JSON schema code that allows one property's value to be dependent on the value of another property.



More specifically, I have two questions A and B. Question B's answer can only be not null when question A has a specific answer. If question A does not have that answer, then the value to question B must be null.



E.g.



A: Do you like cars? Yes/No
B: What is your favourite car?


Question B can only be answered if the answer to question A is "Yes", otherwise it must be left null.



After some research I have found this Stack Overflow thread, which describes the enum and if-then-else approaches to answering this question. The enum is very close to what I need and is defined as below:



{
"type": "object",
"properties": {
"foo": { "enum": ["bar", "baz"] },
"bar": { "type": "string" },
"baz": { "type": "string" }
},
"anyOf": [
{
"properties": {
"foo": { "enum": ["bar"] }
},
"required": ["bar"]
},
{
"properties": {
"foo": { "enum": ["baz"] }
},
"required": ["baz"]
}
]
}


In the above, when the value of Foo is "Bar", then the Bar property is required. Likewise with the value of "Baz". However instead of making the property required, I want to be able to change the type of the property from null to string. Or do something to be able to make the answer to B valid.



Any thoughts on this?










share|improve this question






















  • Hello there! Looks like you're on the right track. Each object in an anyOf array, is in itself a JSON Schema, so you can use all the key words you would normally (including type). If you're using draft-7, you can also use the keyword const rather than enum with a single value.
    – Relequestual
    Nov 8 at 11:49










  • If you want this to be dynamic rather than hard coded, I'm afraid your out of luck, as JSON Schema can't do dynamic validation based on values it doesn't know about specifically in advance (although there is regex). Let me know if this answers your question or not.
    – Relequestual
    Nov 8 at 11:50










  • @Relequestual The only unknown in this situation is going to the answer of the question that the user submits, everything else (the possible question answers etc) are known. In the case of the unknown answer, the answer will always be one value of an enum. So "Yes" and "No" in this case will both be defined by `Enum["Yes, "No"]. Is this behaviour still achievable with JSON Schema?
    – jm123456
    Nov 8 at 13:01










  • As you know all the possible values up front, yes! Don't expect any error message you get back from JSON Schema validation to be useful to the end user though. Did my first comment give you an idea as to how to solve your problem? =]
    – Relequestual
    Nov 8 at 13:49










  • @Relequestual There's only one thing I'm unsure of what to do, how exactly do I change the type of question B when the if of question A has been satisfied? I've tried to highlight what I mean in this gist, please have a look and tell me if you don't understand what I'm trying to say, thanks! (The formatting might be a bit off) gist.github.com/Jakemangan/bf4d20aa3a0f2d2bfbd39dc6e21ca334
    – jm123456
    Nov 8 at 14:07

















up vote
1
down vote

favorite












I'd like to be able to write JSON schema code that allows one property's value to be dependent on the value of another property.



More specifically, I have two questions A and B. Question B's answer can only be not null when question A has a specific answer. If question A does not have that answer, then the value to question B must be null.



E.g.



A: Do you like cars? Yes/No
B: What is your favourite car?


Question B can only be answered if the answer to question A is "Yes", otherwise it must be left null.



After some research I have found this Stack Overflow thread, which describes the enum and if-then-else approaches to answering this question. The enum is very close to what I need and is defined as below:



{
"type": "object",
"properties": {
"foo": { "enum": ["bar", "baz"] },
"bar": { "type": "string" },
"baz": { "type": "string" }
},
"anyOf": [
{
"properties": {
"foo": { "enum": ["bar"] }
},
"required": ["bar"]
},
{
"properties": {
"foo": { "enum": ["baz"] }
},
"required": ["baz"]
}
]
}


In the above, when the value of Foo is "Bar", then the Bar property is required. Likewise with the value of "Baz". However instead of making the property required, I want to be able to change the type of the property from null to string. Or do something to be able to make the answer to B valid.



Any thoughts on this?










share|improve this question






















  • Hello there! Looks like you're on the right track. Each object in an anyOf array, is in itself a JSON Schema, so you can use all the key words you would normally (including type). If you're using draft-7, you can also use the keyword const rather than enum with a single value.
    – Relequestual
    Nov 8 at 11:49










  • If you want this to be dynamic rather than hard coded, I'm afraid your out of luck, as JSON Schema can't do dynamic validation based on values it doesn't know about specifically in advance (although there is regex). Let me know if this answers your question or not.
    – Relequestual
    Nov 8 at 11:50










  • @Relequestual The only unknown in this situation is going to the answer of the question that the user submits, everything else (the possible question answers etc) are known. In the case of the unknown answer, the answer will always be one value of an enum. So "Yes" and "No" in this case will both be defined by `Enum["Yes, "No"]. Is this behaviour still achievable with JSON Schema?
    – jm123456
    Nov 8 at 13:01










  • As you know all the possible values up front, yes! Don't expect any error message you get back from JSON Schema validation to be useful to the end user though. Did my first comment give you an idea as to how to solve your problem? =]
    – Relequestual
    Nov 8 at 13:49










  • @Relequestual There's only one thing I'm unsure of what to do, how exactly do I change the type of question B when the if of question A has been satisfied? I've tried to highlight what I mean in this gist, please have a look and tell me if you don't understand what I'm trying to say, thanks! (The formatting might be a bit off) gist.github.com/Jakemangan/bf4d20aa3a0f2d2bfbd39dc6e21ca334
    – jm123456
    Nov 8 at 14:07















up vote
1
down vote

favorite









up vote
1
down vote

favorite











I'd like to be able to write JSON schema code that allows one property's value to be dependent on the value of another property.



More specifically, I have two questions A and B. Question B's answer can only be not null when question A has a specific answer. If question A does not have that answer, then the value to question B must be null.



E.g.



A: Do you like cars? Yes/No
B: What is your favourite car?


Question B can only be answered if the answer to question A is "Yes", otherwise it must be left null.



After some research I have found this Stack Overflow thread, which describes the enum and if-then-else approaches to answering this question. The enum is very close to what I need and is defined as below:



{
"type": "object",
"properties": {
"foo": { "enum": ["bar", "baz"] },
"bar": { "type": "string" },
"baz": { "type": "string" }
},
"anyOf": [
{
"properties": {
"foo": { "enum": ["bar"] }
},
"required": ["bar"]
},
{
"properties": {
"foo": { "enum": ["baz"] }
},
"required": ["baz"]
}
]
}


In the above, when the value of Foo is "Bar", then the Bar property is required. Likewise with the value of "Baz". However instead of making the property required, I want to be able to change the type of the property from null to string. Or do something to be able to make the answer to B valid.



Any thoughts on this?










share|improve this question













I'd like to be able to write JSON schema code that allows one property's value to be dependent on the value of another property.



More specifically, I have two questions A and B. Question B's answer can only be not null when question A has a specific answer. If question A does not have that answer, then the value to question B must be null.



E.g.



A: Do you like cars? Yes/No
B: What is your favourite car?


Question B can only be answered if the answer to question A is "Yes", otherwise it must be left null.



After some research I have found this Stack Overflow thread, which describes the enum and if-then-else approaches to answering this question. The enum is very close to what I need and is defined as below:



{
"type": "object",
"properties": {
"foo": { "enum": ["bar", "baz"] },
"bar": { "type": "string" },
"baz": { "type": "string" }
},
"anyOf": [
{
"properties": {
"foo": { "enum": ["bar"] }
},
"required": ["bar"]
},
{
"properties": {
"foo": { "enum": ["baz"] }
},
"required": ["baz"]
}
]
}


In the above, when the value of Foo is "Bar", then the Bar property is required. Likewise with the value of "Baz". However instead of making the property required, I want to be able to change the type of the property from null to string. Or do something to be able to make the answer to B valid.



Any thoughts on this?







json jsonschema






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 8 at 11:10









jm123456

61




61












  • Hello there! Looks like you're on the right track. Each object in an anyOf array, is in itself a JSON Schema, so you can use all the key words you would normally (including type). If you're using draft-7, you can also use the keyword const rather than enum with a single value.
    – Relequestual
    Nov 8 at 11:49










  • If you want this to be dynamic rather than hard coded, I'm afraid your out of luck, as JSON Schema can't do dynamic validation based on values it doesn't know about specifically in advance (although there is regex). Let me know if this answers your question or not.
    – Relequestual
    Nov 8 at 11:50










  • @Relequestual The only unknown in this situation is going to the answer of the question that the user submits, everything else (the possible question answers etc) are known. In the case of the unknown answer, the answer will always be one value of an enum. So "Yes" and "No" in this case will both be defined by `Enum["Yes, "No"]. Is this behaviour still achievable with JSON Schema?
    – jm123456
    Nov 8 at 13:01










  • As you know all the possible values up front, yes! Don't expect any error message you get back from JSON Schema validation to be useful to the end user though. Did my first comment give you an idea as to how to solve your problem? =]
    – Relequestual
    Nov 8 at 13:49










  • @Relequestual There's only one thing I'm unsure of what to do, how exactly do I change the type of question B when the if of question A has been satisfied? I've tried to highlight what I mean in this gist, please have a look and tell me if you don't understand what I'm trying to say, thanks! (The formatting might be a bit off) gist.github.com/Jakemangan/bf4d20aa3a0f2d2bfbd39dc6e21ca334
    – jm123456
    Nov 8 at 14:07




















  • Hello there! Looks like you're on the right track. Each object in an anyOf array, is in itself a JSON Schema, so you can use all the key words you would normally (including type). If you're using draft-7, you can also use the keyword const rather than enum with a single value.
    – Relequestual
    Nov 8 at 11:49










  • If you want this to be dynamic rather than hard coded, I'm afraid your out of luck, as JSON Schema can't do dynamic validation based on values it doesn't know about specifically in advance (although there is regex). Let me know if this answers your question or not.
    – Relequestual
    Nov 8 at 11:50










  • @Relequestual The only unknown in this situation is going to the answer of the question that the user submits, everything else (the possible question answers etc) are known. In the case of the unknown answer, the answer will always be one value of an enum. So "Yes" and "No" in this case will both be defined by `Enum["Yes, "No"]. Is this behaviour still achievable with JSON Schema?
    – jm123456
    Nov 8 at 13:01










  • As you know all the possible values up front, yes! Don't expect any error message you get back from JSON Schema validation to be useful to the end user though. Did my first comment give you an idea as to how to solve your problem? =]
    – Relequestual
    Nov 8 at 13:49










  • @Relequestual There's only one thing I'm unsure of what to do, how exactly do I change the type of question B when the if of question A has been satisfied? I've tried to highlight what I mean in this gist, please have a look and tell me if you don't understand what I'm trying to say, thanks! (The formatting might be a bit off) gist.github.com/Jakemangan/bf4d20aa3a0f2d2bfbd39dc6e21ca334
    – jm123456
    Nov 8 at 14:07


















Hello there! Looks like you're on the right track. Each object in an anyOf array, is in itself a JSON Schema, so you can use all the key words you would normally (including type). If you're using draft-7, you can also use the keyword const rather than enum with a single value.
– Relequestual
Nov 8 at 11:49




Hello there! Looks like you're on the right track. Each object in an anyOf array, is in itself a JSON Schema, so you can use all the key words you would normally (including type). If you're using draft-7, you can also use the keyword const rather than enum with a single value.
– Relequestual
Nov 8 at 11:49












If you want this to be dynamic rather than hard coded, I'm afraid your out of luck, as JSON Schema can't do dynamic validation based on values it doesn't know about specifically in advance (although there is regex). Let me know if this answers your question or not.
– Relequestual
Nov 8 at 11:50




If you want this to be dynamic rather than hard coded, I'm afraid your out of luck, as JSON Schema can't do dynamic validation based on values it doesn't know about specifically in advance (although there is regex). Let me know if this answers your question or not.
– Relequestual
Nov 8 at 11:50












@Relequestual The only unknown in this situation is going to the answer of the question that the user submits, everything else (the possible question answers etc) are known. In the case of the unknown answer, the answer will always be one value of an enum. So "Yes" and "No" in this case will both be defined by `Enum["Yes, "No"]. Is this behaviour still achievable with JSON Schema?
– jm123456
Nov 8 at 13:01




@Relequestual The only unknown in this situation is going to the answer of the question that the user submits, everything else (the possible question answers etc) are known. In the case of the unknown answer, the answer will always be one value of an enum. So "Yes" and "No" in this case will both be defined by `Enum["Yes, "No"]. Is this behaviour still achievable with JSON Schema?
– jm123456
Nov 8 at 13:01












As you know all the possible values up front, yes! Don't expect any error message you get back from JSON Schema validation to be useful to the end user though. Did my first comment give you an idea as to how to solve your problem? =]
– Relequestual
Nov 8 at 13:49




As you know all the possible values up front, yes! Don't expect any error message you get back from JSON Schema validation to be useful to the end user though. Did my first comment give you an idea as to how to solve your problem? =]
– Relequestual
Nov 8 at 13:49












@Relequestual There's only one thing I'm unsure of what to do, how exactly do I change the type of question B when the if of question A has been satisfied? I've tried to highlight what I mean in this gist, please have a look and tell me if you don't understand what I'm trying to say, thanks! (The formatting might be a bit off) gist.github.com/Jakemangan/bf4d20aa3a0f2d2bfbd39dc6e21ca334
– jm123456
Nov 8 at 14:07






@Relequestual There's only one thing I'm unsure of what to do, how exactly do I change the type of question B when the if of question A has been satisfied? I've tried to highlight what I mean in this gist, please have a look and tell me if you don't understand what I'm trying to say, thanks! (The formatting might be a bit off) gist.github.com/Jakemangan/bf4d20aa3a0f2d2bfbd39dc6e21ca334
– jm123456
Nov 8 at 14:07














1 Answer
1






active

oldest

votes

















up vote
0
down vote













Did you consider





  1. not defining type of B upfront

  2. using "dependencies" keyword for your schema or making appropriate definition containing answer 'Yes' for question A

  3. and defining question B type only as a result of such dependency?


Let's take your gist:



"questionA": {
"type": "object",
"properties": {
"answer": {
"type": "string",
"minLength": 1,
"enum": ["Yes", "No"]
}
}
}

"questionB": {
"type": "object",
"properties": {
"answer": {
"type": null,
}

}
}


"questions": {
"type": "object",
"properties": {
"A": {"$ref": "#/definitions/questionA"},
"B": {"$ref": "#/definitions/questionB"}
},
"if": {
"properties" : {
"A": {"enum": ["Yes"]}
}
},
"then": {
"B": //Type = string and min length = 1 <-- Unsure what to put here to change the type of QuestionB
}


If I understand correctly your question, the effect you want to achieve is:



If the respondent likes cars, ask him about favourite car and grab answer, else don't bother with the favourite car (and preferrably force the answer to be null).



As Relequestual correctly ponted out in his comment, JSON Schema makes it hard to 'redefine' type. Moreover, each if-then-else content must be a valid schema on it's own.



In order to achieve this effect, you may want to consider following approach:




  1. Define questionA as enum, as you did

  2. Leave property for questionB undefined upfront

  3. Define two possible schemas than can work as property definition for questionB and can be used as a result of dependency

  4. Make use od proper questionB definition in relation to value of questionA


Some sample schema (draft07 compliant) which solves your case is listed below. Also some explanations provided below the schema.



{
"$schema": "http://json-schema.org/draft-07/schema#",
"type" : "object",
"propertyNames" : {
"enum" : [
"questionA",
"questionB",
]
},
"properties" : {
"questionA" : { "$ref" : "#/questionA" },
"questionB" : { "$ref" : "#/questionB" },
},
"dependencies" : {
"questionA" : {
"$ref" : "#/definitions/valid-combinations-of-qA-qB"
}
},
"definitions" : {
"does-like-cars":{
"properties" : {
"questionA" : {
"properties" : {
"answer" : { "enum" : ["Yes","y"] }
}
},
"questionB" : {
"properties" : {
"answer" : {
"$comment" : "Here #/questionB/answer becomes a type:string...",
"$ref" : "#/questionB/definitions/answer-def/string"
}
}
}
},
"required" : ["questionB"]
},
"doesnt-like-cars" :{
"properties" : {
"questionA" : {
"properties" : {
"answer" : { "enum" : ["No","n"] }
}
},
"questionB" : {
"properties" : {
"answer" : {
"$comment" : "Here #/questionB/answer becomes a type:null...",
"$ref" : "#/questionB/definitions/answer-def/null"
}
}
}
}
},
"valid-combinations-of-qA-qB" : {
"anyOf" : [
{ "$ref" : "#/definitions/doesnt-like-cars" },
{ "$ref" : "#/definitions/does-like-cars" }
]
},
},
"examples" : [
{
"questionA" : {
"answer" : "Yes",
},
"questionB" : {
"answer" : "Ass-kicking roadster",
},
},
{
"questionA" : {
"answer" : "No",
},
"questionB" : {
"answer" : null,
},
},
{
},
],
"questionA" : {
"$id" : "#/questionA",
"type" : "object",
"propertyNames" : {
"enum" : ["answer"]
},
"properties" : {
"answer" : {"$ref" : "#/questionA/definitions/answer-def"}
},
"definitions" : {
"answer-def" : {
"$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
"type" : "string",
"enum" : ["Yes", "y", "No", "n"]
}
}
},
"questionB" : {
"$id" : "#/questionB",
"$comment" : "Please note no properties definitions here aside from propertyNames",
"type" : "object",
"propertyNames" : {
"enum" : ["answer"]
},
"definitions" : {
"answer-def" : {
"string" : {
"type" : "string",
"minLength" : 1,
},
"null" : {
"type" : "null"
}
}
}
},
}


Why so complicated?



Because your gist made it so ;-) And more seriously, this is because:




  • In your gist you define both questions as objects. There might be
    valid reason behind it, so I kept it that way (however whenever flat
    list of properties could be used, like "questionA-answer",
    "questionB-answer" I'd prefer it so to keep schema rules less nested,
    thus more readable and easy to create).


  • It seems from your question and gist, that this is important for you
    that "questionB/answer" is null instead of not being validated
    against/ignored when it's not relevant, thus I kept it so



Step by step



Questions as objects



Please note, that I've created separate subschemas for "questionA" and "questionB". This is my personal preference and nothing stops you from getting everything inside "definitions" schema of main schema, however I do it usually that way because:




  • it's easier to split large schema into multiple smaller files after you make everything work like it should (encourages re-use of sub-schemas and helps to structur models in programming languages if someone gets idea to build their data model after my schema)

  • keeps object schemas/sub-schemas properly encapsulated and well, relative referencing is usually more clear to the reader as well

  • helps viewing complex schemas in editors that handle JSON syntax


The "propertyNames"



Since we're working here on "type" : "object" I used "propertyNames" keyword to define schema for allowed property names (since classess in programming languages usually have static sets of properties). Try to enter in each object a property outside of this set - schema valdiation fails. This prevents garbage in your objects. Should it be not desired behaviour, just remove "propertyNames" schemas from each object.



"questionB" - where does the trick with changing type sits?



The trick is: do not define property type and other relevant schema rules upfront. Please note how there's no "properties" schema in "questionB" schema. Instead, I used "definitions" to prepare two possible definitions of "answer" property inside "questionB" object. I will use them depending on "questionA" answer value.



"examples" section?



Some objects, that should illustrate how schema works. Play around with answer values, presence of properties etc. Please note, that an empty object will also pass validation, as no property is required (as in your gist) and there's only one dependency - if "questionA" appears, a "questionB" must show up as well.



Ok, ok, top to bottom now, please



Sure.
So the main schema can have two properties:




  • questionA (an object containing property "answer")


  • questionB (an object containing property "answer")



Is "#/questionA" required? -> No, at least based on your gist.



Is "questionB" required? -> Only if "#/questionA" appears. To add insult to injury :-) the type and allowed values of "#/questionB/answer" strictly depend on the value of "#/questionA/answer".



--> I can safely pre-define main object, foundation for questions objects and will need to define dependency



{
"$schema": "http://json-schema.org/draft-07/schema#",
"type" : "object",
"propertyNames" : {
"enum" : [
"questionA",
"questionB",
]
},
"properties" : {
"questionA" : { "$ref" : "#/questionA" },
"questionB" : { "$ref" : "#/questionB" },
},
"dependencies" : {
"questionA" : {
"$comment" : "when questionA prop appears in validated entity, do something to enforce questionB to be what it wants to be! (like Lady Gaga in Machette...)"
}
},
"questionA" : {
"$id" : "#/questionA",
"type" : "object",
"propertyNames" : {
"enum" : ["answer"]
},
},
"questionB" : {
"$id" : "#/questionB",
"type" : "object",
"propertyNames" : {
"enum" : ["answer"]
},
},
}


Please note I am conciously setting relative base reference via "$id" keyword for question sub-schemas to be able to split schema into multiple smaller files and also for read-ability.



--> I can safely pre-define "questionA/answer" property: type, allowed values etc.



"questionA" : {
"$id" : "#/questionA",
"type" : "object",
"propertyNames" : {
"enum" : ["answer"]
},
"properties" : {
"answer" : {"$ref" : "#/questionA/definitions/answer-def"}
},
"definitions" : {
"answer-def" : {
"$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
"type" : "string",
"enum" : ["Yes", "y", "No", "n"]
}
}
},


Note: I used "definitions" to, well, define schema for specific property. Just in case I'd need to re-use that definition somewhere else... (yep, paranoid about that I am)



--> I can't safely pre-define "#/questionB/answer" property as mentioned above and must do the "trick" part in "#/questionB" sub-schema



"questionB" : {
"$id" : "#/questionB",
"$comment" : "Please note no properties definitions here aside from propertyNames",
"type" : "object",
"propertyNames" : {
"enum" : ["answer"]
},
"definitions" : {
"answer-def" : {
"string" : {
"type" : "string",
"minLength" : 1,
},
"null" : {
"type" : "null"
}
}
}
},


NOTE: See "#/definitions/answer-def"? There are two sub-nodes to that, "#/definitions/answer-def/string" and "#/definitions/answer-def/null" . I wasn't entirely sure how I'll do it at the moment, yet I knew I definitely will need that capability of juggle with "#/questionB/answer" property schema in the end.



--> I must define rules for valid combinations of both answers and since "#/questionB/answer" must be always present; I'm doing that in main schema, which uses questions sub-schemas as it's a cap over them that logically makes a good place to define such rule.



"definitions" : {
"does-like-cars":{
"properties" : {
"questionA" : {
"properties" : {
"answer" : { "enum" : ["Yes","y"] }
}
},
"questionB" : {
"properties" : {
"answer" : {
"$comment" : "Here #/questionB/answer becomes a type:string...",
"$ref" : "#/questionB/definitions/answer-def/string"
}
}
}
},
"required" : ["questionB"]
},
"doesnt-like-cars" :{
"properties" : {
"questionA" : {
"properties" : {
"answer" : { "enum" : ["No","n"] }
}
},
"questionB" : {
"properties" : {
"answer" : {
"$comment" : "Here #/questionB/answer becomes a type:null...",
"$ref" : "#/questionB/definitions/answer-def/null"
}
}
}
}
},
"valid-combinations-of-qA-qB" : {
"anyOf" : [
{ "$ref" : "#/definitions/doesnt-like-cars" },
{ "$ref" : "#/definitions/does-like-cars" }
]
},


So there are those, who like cars - I basically define allowed values of "#/questionA/answer" and relevant definition of property of "#/questionB/answer". Since this is the schema, both sets must match to fulfill this definition. Please note I marked "questionB" property key as required in order to not validate JSON that contains only "questionA" property key against schema.



I did similar thing for those, who don't like cars (how one cannot like cars?! Wicked times...) and at the end I said in "valid-combinations-of-qA-qB": It's either or, people. Either you like cars and give me the answer or you don't like cars and the answer must be null. "XOR" ("oneOf") comes to mind automatically but since I've defined like cars AND answer and doesn't like cars AND answer = null as a complete schemas, logical OR is completely sufficient -> "anyOf".



At the end the finishing touch was to use that rule in "dependencies" section of main schema, which translates to: if "questionA" appears in validated instance, either... or...



"dependencies" : {
"questionA" : {
"$ref" : "#/definitions/valid-combinations-of-qA-qB"
}
},


Hope it clarifies and helps with your case.



Open questions



Why not use object "answers" with properties reflecting each question answer, with key identifying the question? It could simplify a bit rules and references with regards to dependencies between answers (less typing, yep, I'm a lazy lad).



Why "#/questionB/answer" must be null instead just ignoring it if "#/questionA/answer" : { "enum" : ["No"] } ?



Recommended reading



See "Understanding JSON Schema" : https://json-schema.org/understanding-json-schema/index.html



Some basic examples: https://json-schema.org/learn/



JSON schema validation reference: https://json-schema.org/latest/json-schema-validation.html



A lot of StackOverflow Q&A provides nice insight in how to manage different cases with JSON Schema.



Also it might be helpful at occasion to check for relative JSON Pointers RFC.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53206547%2fjson-schema-property-dependent-on-value-of-previous-property%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote













    Did you consider





    1. not defining type of B upfront

    2. using "dependencies" keyword for your schema or making appropriate definition containing answer 'Yes' for question A

    3. and defining question B type only as a result of such dependency?


    Let's take your gist:



    "questionA": {
    "type": "object",
    "properties": {
    "answer": {
    "type": "string",
    "minLength": 1,
    "enum": ["Yes", "No"]
    }
    }
    }

    "questionB": {
    "type": "object",
    "properties": {
    "answer": {
    "type": null,
    }

    }
    }


    "questions": {
    "type": "object",
    "properties": {
    "A": {"$ref": "#/definitions/questionA"},
    "B": {"$ref": "#/definitions/questionB"}
    },
    "if": {
    "properties" : {
    "A": {"enum": ["Yes"]}
    }
    },
    "then": {
    "B": //Type = string and min length = 1 <-- Unsure what to put here to change the type of QuestionB
    }


    If I understand correctly your question, the effect you want to achieve is:



    If the respondent likes cars, ask him about favourite car and grab answer, else don't bother with the favourite car (and preferrably force the answer to be null).



    As Relequestual correctly ponted out in his comment, JSON Schema makes it hard to 'redefine' type. Moreover, each if-then-else content must be a valid schema on it's own.



    In order to achieve this effect, you may want to consider following approach:




    1. Define questionA as enum, as you did

    2. Leave property for questionB undefined upfront

    3. Define two possible schemas than can work as property definition for questionB and can be used as a result of dependency

    4. Make use od proper questionB definition in relation to value of questionA


    Some sample schema (draft07 compliant) which solves your case is listed below. Also some explanations provided below the schema.



    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type" : "object",
    "propertyNames" : {
    "enum" : [
    "questionA",
    "questionB",
    ]
    },
    "properties" : {
    "questionA" : { "$ref" : "#/questionA" },
    "questionB" : { "$ref" : "#/questionB" },
    },
    "dependencies" : {
    "questionA" : {
    "$ref" : "#/definitions/valid-combinations-of-qA-qB"
    }
    },
    "definitions" : {
    "does-like-cars":{
    "properties" : {
    "questionA" : {
    "properties" : {
    "answer" : { "enum" : ["Yes","y"] }
    }
    },
    "questionB" : {
    "properties" : {
    "answer" : {
    "$comment" : "Here #/questionB/answer becomes a type:string...",
    "$ref" : "#/questionB/definitions/answer-def/string"
    }
    }
    }
    },
    "required" : ["questionB"]
    },
    "doesnt-like-cars" :{
    "properties" : {
    "questionA" : {
    "properties" : {
    "answer" : { "enum" : ["No","n"] }
    }
    },
    "questionB" : {
    "properties" : {
    "answer" : {
    "$comment" : "Here #/questionB/answer becomes a type:null...",
    "$ref" : "#/questionB/definitions/answer-def/null"
    }
    }
    }
    }
    },
    "valid-combinations-of-qA-qB" : {
    "anyOf" : [
    { "$ref" : "#/definitions/doesnt-like-cars" },
    { "$ref" : "#/definitions/does-like-cars" }
    ]
    },
    },
    "examples" : [
    {
    "questionA" : {
    "answer" : "Yes",
    },
    "questionB" : {
    "answer" : "Ass-kicking roadster",
    },
    },
    {
    "questionA" : {
    "answer" : "No",
    },
    "questionB" : {
    "answer" : null,
    },
    },
    {
    },
    ],
    "questionA" : {
    "$id" : "#/questionA",
    "type" : "object",
    "propertyNames" : {
    "enum" : ["answer"]
    },
    "properties" : {
    "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
    },
    "definitions" : {
    "answer-def" : {
    "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
    "type" : "string",
    "enum" : ["Yes", "y", "No", "n"]
    }
    }
    },
    "questionB" : {
    "$id" : "#/questionB",
    "$comment" : "Please note no properties definitions here aside from propertyNames",
    "type" : "object",
    "propertyNames" : {
    "enum" : ["answer"]
    },
    "definitions" : {
    "answer-def" : {
    "string" : {
    "type" : "string",
    "minLength" : 1,
    },
    "null" : {
    "type" : "null"
    }
    }
    }
    },
    }


    Why so complicated?



    Because your gist made it so ;-) And more seriously, this is because:




    • In your gist you define both questions as objects. There might be
      valid reason behind it, so I kept it that way (however whenever flat
      list of properties could be used, like "questionA-answer",
      "questionB-answer" I'd prefer it so to keep schema rules less nested,
      thus more readable and easy to create).


    • It seems from your question and gist, that this is important for you
      that "questionB/answer" is null instead of not being validated
      against/ignored when it's not relevant, thus I kept it so



    Step by step



    Questions as objects



    Please note, that I've created separate subschemas for "questionA" and "questionB". This is my personal preference and nothing stops you from getting everything inside "definitions" schema of main schema, however I do it usually that way because:




    • it's easier to split large schema into multiple smaller files after you make everything work like it should (encourages re-use of sub-schemas and helps to structur models in programming languages if someone gets idea to build their data model after my schema)

    • keeps object schemas/sub-schemas properly encapsulated and well, relative referencing is usually more clear to the reader as well

    • helps viewing complex schemas in editors that handle JSON syntax


    The "propertyNames"



    Since we're working here on "type" : "object" I used "propertyNames" keyword to define schema for allowed property names (since classess in programming languages usually have static sets of properties). Try to enter in each object a property outside of this set - schema valdiation fails. This prevents garbage in your objects. Should it be not desired behaviour, just remove "propertyNames" schemas from each object.



    "questionB" - where does the trick with changing type sits?



    The trick is: do not define property type and other relevant schema rules upfront. Please note how there's no "properties" schema in "questionB" schema. Instead, I used "definitions" to prepare two possible definitions of "answer" property inside "questionB" object. I will use them depending on "questionA" answer value.



    "examples" section?



    Some objects, that should illustrate how schema works. Play around with answer values, presence of properties etc. Please note, that an empty object will also pass validation, as no property is required (as in your gist) and there's only one dependency - if "questionA" appears, a "questionB" must show up as well.



    Ok, ok, top to bottom now, please



    Sure.
    So the main schema can have two properties:




    • questionA (an object containing property "answer")


    • questionB (an object containing property "answer")



    Is "#/questionA" required? -> No, at least based on your gist.



    Is "questionB" required? -> Only if "#/questionA" appears. To add insult to injury :-) the type and allowed values of "#/questionB/answer" strictly depend on the value of "#/questionA/answer".



    --> I can safely pre-define main object, foundation for questions objects and will need to define dependency



    {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type" : "object",
    "propertyNames" : {
    "enum" : [
    "questionA",
    "questionB",
    ]
    },
    "properties" : {
    "questionA" : { "$ref" : "#/questionA" },
    "questionB" : { "$ref" : "#/questionB" },
    },
    "dependencies" : {
    "questionA" : {
    "$comment" : "when questionA prop appears in validated entity, do something to enforce questionB to be what it wants to be! (like Lady Gaga in Machette...)"
    }
    },
    "questionA" : {
    "$id" : "#/questionA",
    "type" : "object",
    "propertyNames" : {
    "enum" : ["answer"]
    },
    },
    "questionB" : {
    "$id" : "#/questionB",
    "type" : "object",
    "propertyNames" : {
    "enum" : ["answer"]
    },
    },
    }


    Please note I am conciously setting relative base reference via "$id" keyword for question sub-schemas to be able to split schema into multiple smaller files and also for read-ability.



    --> I can safely pre-define "questionA/answer" property: type, allowed values etc.



    "questionA" : {
    "$id" : "#/questionA",
    "type" : "object",
    "propertyNames" : {
    "enum" : ["answer"]
    },
    "properties" : {
    "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
    },
    "definitions" : {
    "answer-def" : {
    "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
    "type" : "string",
    "enum" : ["Yes", "y", "No", "n"]
    }
    }
    },


    Note: I used "definitions" to, well, define schema for specific property. Just in case I'd need to re-use that definition somewhere else... (yep, paranoid about that I am)



    --> I can't safely pre-define "#/questionB/answer" property as mentioned above and must do the "trick" part in "#/questionB" sub-schema



    "questionB" : {
    "$id" : "#/questionB",
    "$comment" : "Please note no properties definitions here aside from propertyNames",
    "type" : "object",
    "propertyNames" : {
    "enum" : ["answer"]
    },
    "definitions" : {
    "answer-def" : {
    "string" : {
    "type" : "string",
    "minLength" : 1,
    },
    "null" : {
    "type" : "null"
    }
    }
    }
    },


    NOTE: See "#/definitions/answer-def"? There are two sub-nodes to that, "#/definitions/answer-def/string" and "#/definitions/answer-def/null" . I wasn't entirely sure how I'll do it at the moment, yet I knew I definitely will need that capability of juggle with "#/questionB/answer" property schema in the end.



    --> I must define rules for valid combinations of both answers and since "#/questionB/answer" must be always present; I'm doing that in main schema, which uses questions sub-schemas as it's a cap over them that logically makes a good place to define such rule.



    "definitions" : {
    "does-like-cars":{
    "properties" : {
    "questionA" : {
    "properties" : {
    "answer" : { "enum" : ["Yes","y"] }
    }
    },
    "questionB" : {
    "properties" : {
    "answer" : {
    "$comment" : "Here #/questionB/answer becomes a type:string...",
    "$ref" : "#/questionB/definitions/answer-def/string"
    }
    }
    }
    },
    "required" : ["questionB"]
    },
    "doesnt-like-cars" :{
    "properties" : {
    "questionA" : {
    "properties" : {
    "answer" : { "enum" : ["No","n"] }
    }
    },
    "questionB" : {
    "properties" : {
    "answer" : {
    "$comment" : "Here #/questionB/answer becomes a type:null...",
    "$ref" : "#/questionB/definitions/answer-def/null"
    }
    }
    }
    }
    },
    "valid-combinations-of-qA-qB" : {
    "anyOf" : [
    { "$ref" : "#/definitions/doesnt-like-cars" },
    { "$ref" : "#/definitions/does-like-cars" }
    ]
    },


    So there are those, who like cars - I basically define allowed values of "#/questionA/answer" and relevant definition of property of "#/questionB/answer". Since this is the schema, both sets must match to fulfill this definition. Please note I marked "questionB" property key as required in order to not validate JSON that contains only "questionA" property key against schema.



    I did similar thing for those, who don't like cars (how one cannot like cars?! Wicked times...) and at the end I said in "valid-combinations-of-qA-qB": It's either or, people. Either you like cars and give me the answer or you don't like cars and the answer must be null. "XOR" ("oneOf") comes to mind automatically but since I've defined like cars AND answer and doesn't like cars AND answer = null as a complete schemas, logical OR is completely sufficient -> "anyOf".



    At the end the finishing touch was to use that rule in "dependencies" section of main schema, which translates to: if "questionA" appears in validated instance, either... or...



    "dependencies" : {
    "questionA" : {
    "$ref" : "#/definitions/valid-combinations-of-qA-qB"
    }
    },


    Hope it clarifies and helps with your case.



    Open questions



    Why not use object "answers" with properties reflecting each question answer, with key identifying the question? It could simplify a bit rules and references with regards to dependencies between answers (less typing, yep, I'm a lazy lad).



    Why "#/questionB/answer" must be null instead just ignoring it if "#/questionA/answer" : { "enum" : ["No"] } ?



    Recommended reading



    See "Understanding JSON Schema" : https://json-schema.org/understanding-json-schema/index.html



    Some basic examples: https://json-schema.org/learn/



    JSON schema validation reference: https://json-schema.org/latest/json-schema-validation.html



    A lot of StackOverflow Q&A provides nice insight in how to manage different cases with JSON Schema.



    Also it might be helpful at occasion to check for relative JSON Pointers RFC.






    share|improve this answer



























      up vote
      0
      down vote













      Did you consider





      1. not defining type of B upfront

      2. using "dependencies" keyword for your schema or making appropriate definition containing answer 'Yes' for question A

      3. and defining question B type only as a result of such dependency?


      Let's take your gist:



      "questionA": {
      "type": "object",
      "properties": {
      "answer": {
      "type": "string",
      "minLength": 1,
      "enum": ["Yes", "No"]
      }
      }
      }

      "questionB": {
      "type": "object",
      "properties": {
      "answer": {
      "type": null,
      }

      }
      }


      "questions": {
      "type": "object",
      "properties": {
      "A": {"$ref": "#/definitions/questionA"},
      "B": {"$ref": "#/definitions/questionB"}
      },
      "if": {
      "properties" : {
      "A": {"enum": ["Yes"]}
      }
      },
      "then": {
      "B": //Type = string and min length = 1 <-- Unsure what to put here to change the type of QuestionB
      }


      If I understand correctly your question, the effect you want to achieve is:



      If the respondent likes cars, ask him about favourite car and grab answer, else don't bother with the favourite car (and preferrably force the answer to be null).



      As Relequestual correctly ponted out in his comment, JSON Schema makes it hard to 'redefine' type. Moreover, each if-then-else content must be a valid schema on it's own.



      In order to achieve this effect, you may want to consider following approach:




      1. Define questionA as enum, as you did

      2. Leave property for questionB undefined upfront

      3. Define two possible schemas than can work as property definition for questionB and can be used as a result of dependency

      4. Make use od proper questionB definition in relation to value of questionA


      Some sample schema (draft07 compliant) which solves your case is listed below. Also some explanations provided below the schema.



      {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type" : "object",
      "propertyNames" : {
      "enum" : [
      "questionA",
      "questionB",
      ]
      },
      "properties" : {
      "questionA" : { "$ref" : "#/questionA" },
      "questionB" : { "$ref" : "#/questionB" },
      },
      "dependencies" : {
      "questionA" : {
      "$ref" : "#/definitions/valid-combinations-of-qA-qB"
      }
      },
      "definitions" : {
      "does-like-cars":{
      "properties" : {
      "questionA" : {
      "properties" : {
      "answer" : { "enum" : ["Yes","y"] }
      }
      },
      "questionB" : {
      "properties" : {
      "answer" : {
      "$comment" : "Here #/questionB/answer becomes a type:string...",
      "$ref" : "#/questionB/definitions/answer-def/string"
      }
      }
      }
      },
      "required" : ["questionB"]
      },
      "doesnt-like-cars" :{
      "properties" : {
      "questionA" : {
      "properties" : {
      "answer" : { "enum" : ["No","n"] }
      }
      },
      "questionB" : {
      "properties" : {
      "answer" : {
      "$comment" : "Here #/questionB/answer becomes a type:null...",
      "$ref" : "#/questionB/definitions/answer-def/null"
      }
      }
      }
      }
      },
      "valid-combinations-of-qA-qB" : {
      "anyOf" : [
      { "$ref" : "#/definitions/doesnt-like-cars" },
      { "$ref" : "#/definitions/does-like-cars" }
      ]
      },
      },
      "examples" : [
      {
      "questionA" : {
      "answer" : "Yes",
      },
      "questionB" : {
      "answer" : "Ass-kicking roadster",
      },
      },
      {
      "questionA" : {
      "answer" : "No",
      },
      "questionB" : {
      "answer" : null,
      },
      },
      {
      },
      ],
      "questionA" : {
      "$id" : "#/questionA",
      "type" : "object",
      "propertyNames" : {
      "enum" : ["answer"]
      },
      "properties" : {
      "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
      },
      "definitions" : {
      "answer-def" : {
      "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
      "type" : "string",
      "enum" : ["Yes", "y", "No", "n"]
      }
      }
      },
      "questionB" : {
      "$id" : "#/questionB",
      "$comment" : "Please note no properties definitions here aside from propertyNames",
      "type" : "object",
      "propertyNames" : {
      "enum" : ["answer"]
      },
      "definitions" : {
      "answer-def" : {
      "string" : {
      "type" : "string",
      "minLength" : 1,
      },
      "null" : {
      "type" : "null"
      }
      }
      }
      },
      }


      Why so complicated?



      Because your gist made it so ;-) And more seriously, this is because:




      • In your gist you define both questions as objects. There might be
        valid reason behind it, so I kept it that way (however whenever flat
        list of properties could be used, like "questionA-answer",
        "questionB-answer" I'd prefer it so to keep schema rules less nested,
        thus more readable and easy to create).


      • It seems from your question and gist, that this is important for you
        that "questionB/answer" is null instead of not being validated
        against/ignored when it's not relevant, thus I kept it so



      Step by step



      Questions as objects



      Please note, that I've created separate subschemas for "questionA" and "questionB". This is my personal preference and nothing stops you from getting everything inside "definitions" schema of main schema, however I do it usually that way because:




      • it's easier to split large schema into multiple smaller files after you make everything work like it should (encourages re-use of sub-schemas and helps to structur models in programming languages if someone gets idea to build their data model after my schema)

      • keeps object schemas/sub-schemas properly encapsulated and well, relative referencing is usually more clear to the reader as well

      • helps viewing complex schemas in editors that handle JSON syntax


      The "propertyNames"



      Since we're working here on "type" : "object" I used "propertyNames" keyword to define schema for allowed property names (since classess in programming languages usually have static sets of properties). Try to enter in each object a property outside of this set - schema valdiation fails. This prevents garbage in your objects. Should it be not desired behaviour, just remove "propertyNames" schemas from each object.



      "questionB" - where does the trick with changing type sits?



      The trick is: do not define property type and other relevant schema rules upfront. Please note how there's no "properties" schema in "questionB" schema. Instead, I used "definitions" to prepare two possible definitions of "answer" property inside "questionB" object. I will use them depending on "questionA" answer value.



      "examples" section?



      Some objects, that should illustrate how schema works. Play around with answer values, presence of properties etc. Please note, that an empty object will also pass validation, as no property is required (as in your gist) and there's only one dependency - if "questionA" appears, a "questionB" must show up as well.



      Ok, ok, top to bottom now, please



      Sure.
      So the main schema can have two properties:




      • questionA (an object containing property "answer")


      • questionB (an object containing property "answer")



      Is "#/questionA" required? -> No, at least based on your gist.



      Is "questionB" required? -> Only if "#/questionA" appears. To add insult to injury :-) the type and allowed values of "#/questionB/answer" strictly depend on the value of "#/questionA/answer".



      --> I can safely pre-define main object, foundation for questions objects and will need to define dependency



      {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type" : "object",
      "propertyNames" : {
      "enum" : [
      "questionA",
      "questionB",
      ]
      },
      "properties" : {
      "questionA" : { "$ref" : "#/questionA" },
      "questionB" : { "$ref" : "#/questionB" },
      },
      "dependencies" : {
      "questionA" : {
      "$comment" : "when questionA prop appears in validated entity, do something to enforce questionB to be what it wants to be! (like Lady Gaga in Machette...)"
      }
      },
      "questionA" : {
      "$id" : "#/questionA",
      "type" : "object",
      "propertyNames" : {
      "enum" : ["answer"]
      },
      },
      "questionB" : {
      "$id" : "#/questionB",
      "type" : "object",
      "propertyNames" : {
      "enum" : ["answer"]
      },
      },
      }


      Please note I am conciously setting relative base reference via "$id" keyword for question sub-schemas to be able to split schema into multiple smaller files and also for read-ability.



      --> I can safely pre-define "questionA/answer" property: type, allowed values etc.



      "questionA" : {
      "$id" : "#/questionA",
      "type" : "object",
      "propertyNames" : {
      "enum" : ["answer"]
      },
      "properties" : {
      "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
      },
      "definitions" : {
      "answer-def" : {
      "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
      "type" : "string",
      "enum" : ["Yes", "y", "No", "n"]
      }
      }
      },


      Note: I used "definitions" to, well, define schema for specific property. Just in case I'd need to re-use that definition somewhere else... (yep, paranoid about that I am)



      --> I can't safely pre-define "#/questionB/answer" property as mentioned above and must do the "trick" part in "#/questionB" sub-schema



      "questionB" : {
      "$id" : "#/questionB",
      "$comment" : "Please note no properties definitions here aside from propertyNames",
      "type" : "object",
      "propertyNames" : {
      "enum" : ["answer"]
      },
      "definitions" : {
      "answer-def" : {
      "string" : {
      "type" : "string",
      "minLength" : 1,
      },
      "null" : {
      "type" : "null"
      }
      }
      }
      },


      NOTE: See "#/definitions/answer-def"? There are two sub-nodes to that, "#/definitions/answer-def/string" and "#/definitions/answer-def/null" . I wasn't entirely sure how I'll do it at the moment, yet I knew I definitely will need that capability of juggle with "#/questionB/answer" property schema in the end.



      --> I must define rules for valid combinations of both answers and since "#/questionB/answer" must be always present; I'm doing that in main schema, which uses questions sub-schemas as it's a cap over them that logically makes a good place to define such rule.



      "definitions" : {
      "does-like-cars":{
      "properties" : {
      "questionA" : {
      "properties" : {
      "answer" : { "enum" : ["Yes","y"] }
      }
      },
      "questionB" : {
      "properties" : {
      "answer" : {
      "$comment" : "Here #/questionB/answer becomes a type:string...",
      "$ref" : "#/questionB/definitions/answer-def/string"
      }
      }
      }
      },
      "required" : ["questionB"]
      },
      "doesnt-like-cars" :{
      "properties" : {
      "questionA" : {
      "properties" : {
      "answer" : { "enum" : ["No","n"] }
      }
      },
      "questionB" : {
      "properties" : {
      "answer" : {
      "$comment" : "Here #/questionB/answer becomes a type:null...",
      "$ref" : "#/questionB/definitions/answer-def/null"
      }
      }
      }
      }
      },
      "valid-combinations-of-qA-qB" : {
      "anyOf" : [
      { "$ref" : "#/definitions/doesnt-like-cars" },
      { "$ref" : "#/definitions/does-like-cars" }
      ]
      },


      So there are those, who like cars - I basically define allowed values of "#/questionA/answer" and relevant definition of property of "#/questionB/answer". Since this is the schema, both sets must match to fulfill this definition. Please note I marked "questionB" property key as required in order to not validate JSON that contains only "questionA" property key against schema.



      I did similar thing for those, who don't like cars (how one cannot like cars?! Wicked times...) and at the end I said in "valid-combinations-of-qA-qB": It's either or, people. Either you like cars and give me the answer or you don't like cars and the answer must be null. "XOR" ("oneOf") comes to mind automatically but since I've defined like cars AND answer and doesn't like cars AND answer = null as a complete schemas, logical OR is completely sufficient -> "anyOf".



      At the end the finishing touch was to use that rule in "dependencies" section of main schema, which translates to: if "questionA" appears in validated instance, either... or...



      "dependencies" : {
      "questionA" : {
      "$ref" : "#/definitions/valid-combinations-of-qA-qB"
      }
      },


      Hope it clarifies and helps with your case.



      Open questions



      Why not use object "answers" with properties reflecting each question answer, with key identifying the question? It could simplify a bit rules and references with regards to dependencies between answers (less typing, yep, I'm a lazy lad).



      Why "#/questionB/answer" must be null instead just ignoring it if "#/questionA/answer" : { "enum" : ["No"] } ?



      Recommended reading



      See "Understanding JSON Schema" : https://json-schema.org/understanding-json-schema/index.html



      Some basic examples: https://json-schema.org/learn/



      JSON schema validation reference: https://json-schema.org/latest/json-schema-validation.html



      A lot of StackOverflow Q&A provides nice insight in how to manage different cases with JSON Schema.



      Also it might be helpful at occasion to check for relative JSON Pointers RFC.






      share|improve this answer

























        up vote
        0
        down vote










        up vote
        0
        down vote









        Did you consider





        1. not defining type of B upfront

        2. using "dependencies" keyword for your schema or making appropriate definition containing answer 'Yes' for question A

        3. and defining question B type only as a result of such dependency?


        Let's take your gist:



        "questionA": {
        "type": "object",
        "properties": {
        "answer": {
        "type": "string",
        "minLength": 1,
        "enum": ["Yes", "No"]
        }
        }
        }

        "questionB": {
        "type": "object",
        "properties": {
        "answer": {
        "type": null,
        }

        }
        }


        "questions": {
        "type": "object",
        "properties": {
        "A": {"$ref": "#/definitions/questionA"},
        "B": {"$ref": "#/definitions/questionB"}
        },
        "if": {
        "properties" : {
        "A": {"enum": ["Yes"]}
        }
        },
        "then": {
        "B": //Type = string and min length = 1 <-- Unsure what to put here to change the type of QuestionB
        }


        If I understand correctly your question, the effect you want to achieve is:



        If the respondent likes cars, ask him about favourite car and grab answer, else don't bother with the favourite car (and preferrably force the answer to be null).



        As Relequestual correctly ponted out in his comment, JSON Schema makes it hard to 'redefine' type. Moreover, each if-then-else content must be a valid schema on it's own.



        In order to achieve this effect, you may want to consider following approach:




        1. Define questionA as enum, as you did

        2. Leave property for questionB undefined upfront

        3. Define two possible schemas than can work as property definition for questionB and can be used as a result of dependency

        4. Make use od proper questionB definition in relation to value of questionA


        Some sample schema (draft07 compliant) which solves your case is listed below. Also some explanations provided below the schema.



        {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type" : "object",
        "propertyNames" : {
        "enum" : [
        "questionA",
        "questionB",
        ]
        },
        "properties" : {
        "questionA" : { "$ref" : "#/questionA" },
        "questionB" : { "$ref" : "#/questionB" },
        },
        "dependencies" : {
        "questionA" : {
        "$ref" : "#/definitions/valid-combinations-of-qA-qB"
        }
        },
        "definitions" : {
        "does-like-cars":{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["Yes","y"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:string...",
        "$ref" : "#/questionB/definitions/answer-def/string"
        }
        }
        }
        },
        "required" : ["questionB"]
        },
        "doesnt-like-cars" :{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["No","n"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:null...",
        "$ref" : "#/questionB/definitions/answer-def/null"
        }
        }
        }
        }
        },
        "valid-combinations-of-qA-qB" : {
        "anyOf" : [
        { "$ref" : "#/definitions/doesnt-like-cars" },
        { "$ref" : "#/definitions/does-like-cars" }
        ]
        },
        },
        "examples" : [
        {
        "questionA" : {
        "answer" : "Yes",
        },
        "questionB" : {
        "answer" : "Ass-kicking roadster",
        },
        },
        {
        "questionA" : {
        "answer" : "No",
        },
        "questionB" : {
        "answer" : null,
        },
        },
        {
        },
        ],
        "questionA" : {
        "$id" : "#/questionA",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "properties" : {
        "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
        },
        "definitions" : {
        "answer-def" : {
        "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
        "type" : "string",
        "enum" : ["Yes", "y", "No", "n"]
        }
        }
        },
        "questionB" : {
        "$id" : "#/questionB",
        "$comment" : "Please note no properties definitions here aside from propertyNames",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "definitions" : {
        "answer-def" : {
        "string" : {
        "type" : "string",
        "minLength" : 1,
        },
        "null" : {
        "type" : "null"
        }
        }
        }
        },
        }


        Why so complicated?



        Because your gist made it so ;-) And more seriously, this is because:




        • In your gist you define both questions as objects. There might be
          valid reason behind it, so I kept it that way (however whenever flat
          list of properties could be used, like "questionA-answer",
          "questionB-answer" I'd prefer it so to keep schema rules less nested,
          thus more readable and easy to create).


        • It seems from your question and gist, that this is important for you
          that "questionB/answer" is null instead of not being validated
          against/ignored when it's not relevant, thus I kept it so



        Step by step



        Questions as objects



        Please note, that I've created separate subschemas for "questionA" and "questionB". This is my personal preference and nothing stops you from getting everything inside "definitions" schema of main schema, however I do it usually that way because:




        • it's easier to split large schema into multiple smaller files after you make everything work like it should (encourages re-use of sub-schemas and helps to structur models in programming languages if someone gets idea to build their data model after my schema)

        • keeps object schemas/sub-schemas properly encapsulated and well, relative referencing is usually more clear to the reader as well

        • helps viewing complex schemas in editors that handle JSON syntax


        The "propertyNames"



        Since we're working here on "type" : "object" I used "propertyNames" keyword to define schema for allowed property names (since classess in programming languages usually have static sets of properties). Try to enter in each object a property outside of this set - schema valdiation fails. This prevents garbage in your objects. Should it be not desired behaviour, just remove "propertyNames" schemas from each object.



        "questionB" - where does the trick with changing type sits?



        The trick is: do not define property type and other relevant schema rules upfront. Please note how there's no "properties" schema in "questionB" schema. Instead, I used "definitions" to prepare two possible definitions of "answer" property inside "questionB" object. I will use them depending on "questionA" answer value.



        "examples" section?



        Some objects, that should illustrate how schema works. Play around with answer values, presence of properties etc. Please note, that an empty object will also pass validation, as no property is required (as in your gist) and there's only one dependency - if "questionA" appears, a "questionB" must show up as well.



        Ok, ok, top to bottom now, please



        Sure.
        So the main schema can have two properties:




        • questionA (an object containing property "answer")


        • questionB (an object containing property "answer")



        Is "#/questionA" required? -> No, at least based on your gist.



        Is "questionB" required? -> Only if "#/questionA" appears. To add insult to injury :-) the type and allowed values of "#/questionB/answer" strictly depend on the value of "#/questionA/answer".



        --> I can safely pre-define main object, foundation for questions objects and will need to define dependency



        {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type" : "object",
        "propertyNames" : {
        "enum" : [
        "questionA",
        "questionB",
        ]
        },
        "properties" : {
        "questionA" : { "$ref" : "#/questionA" },
        "questionB" : { "$ref" : "#/questionB" },
        },
        "dependencies" : {
        "questionA" : {
        "$comment" : "when questionA prop appears in validated entity, do something to enforce questionB to be what it wants to be! (like Lady Gaga in Machette...)"
        }
        },
        "questionA" : {
        "$id" : "#/questionA",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        },
        "questionB" : {
        "$id" : "#/questionB",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        },
        }


        Please note I am conciously setting relative base reference via "$id" keyword for question sub-schemas to be able to split schema into multiple smaller files and also for read-ability.



        --> I can safely pre-define "questionA/answer" property: type, allowed values etc.



        "questionA" : {
        "$id" : "#/questionA",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "properties" : {
        "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
        },
        "definitions" : {
        "answer-def" : {
        "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
        "type" : "string",
        "enum" : ["Yes", "y", "No", "n"]
        }
        }
        },


        Note: I used "definitions" to, well, define schema for specific property. Just in case I'd need to re-use that definition somewhere else... (yep, paranoid about that I am)



        --> I can't safely pre-define "#/questionB/answer" property as mentioned above and must do the "trick" part in "#/questionB" sub-schema



        "questionB" : {
        "$id" : "#/questionB",
        "$comment" : "Please note no properties definitions here aside from propertyNames",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "definitions" : {
        "answer-def" : {
        "string" : {
        "type" : "string",
        "minLength" : 1,
        },
        "null" : {
        "type" : "null"
        }
        }
        }
        },


        NOTE: See "#/definitions/answer-def"? There are two sub-nodes to that, "#/definitions/answer-def/string" and "#/definitions/answer-def/null" . I wasn't entirely sure how I'll do it at the moment, yet I knew I definitely will need that capability of juggle with "#/questionB/answer" property schema in the end.



        --> I must define rules for valid combinations of both answers and since "#/questionB/answer" must be always present; I'm doing that in main schema, which uses questions sub-schemas as it's a cap over them that logically makes a good place to define such rule.



        "definitions" : {
        "does-like-cars":{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["Yes","y"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:string...",
        "$ref" : "#/questionB/definitions/answer-def/string"
        }
        }
        }
        },
        "required" : ["questionB"]
        },
        "doesnt-like-cars" :{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["No","n"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:null...",
        "$ref" : "#/questionB/definitions/answer-def/null"
        }
        }
        }
        }
        },
        "valid-combinations-of-qA-qB" : {
        "anyOf" : [
        { "$ref" : "#/definitions/doesnt-like-cars" },
        { "$ref" : "#/definitions/does-like-cars" }
        ]
        },


        So there are those, who like cars - I basically define allowed values of "#/questionA/answer" and relevant definition of property of "#/questionB/answer". Since this is the schema, both sets must match to fulfill this definition. Please note I marked "questionB" property key as required in order to not validate JSON that contains only "questionA" property key against schema.



        I did similar thing for those, who don't like cars (how one cannot like cars?! Wicked times...) and at the end I said in "valid-combinations-of-qA-qB": It's either or, people. Either you like cars and give me the answer or you don't like cars and the answer must be null. "XOR" ("oneOf") comes to mind automatically but since I've defined like cars AND answer and doesn't like cars AND answer = null as a complete schemas, logical OR is completely sufficient -> "anyOf".



        At the end the finishing touch was to use that rule in "dependencies" section of main schema, which translates to: if "questionA" appears in validated instance, either... or...



        "dependencies" : {
        "questionA" : {
        "$ref" : "#/definitions/valid-combinations-of-qA-qB"
        }
        },


        Hope it clarifies and helps with your case.



        Open questions



        Why not use object "answers" with properties reflecting each question answer, with key identifying the question? It could simplify a bit rules and references with regards to dependencies between answers (less typing, yep, I'm a lazy lad).



        Why "#/questionB/answer" must be null instead just ignoring it if "#/questionA/answer" : { "enum" : ["No"] } ?



        Recommended reading



        See "Understanding JSON Schema" : https://json-schema.org/understanding-json-schema/index.html



        Some basic examples: https://json-schema.org/learn/



        JSON schema validation reference: https://json-schema.org/latest/json-schema-validation.html



        A lot of StackOverflow Q&A provides nice insight in how to manage different cases with JSON Schema.



        Also it might be helpful at occasion to check for relative JSON Pointers RFC.






        share|improve this answer














        Did you consider





        1. not defining type of B upfront

        2. using "dependencies" keyword for your schema or making appropriate definition containing answer 'Yes' for question A

        3. and defining question B type only as a result of such dependency?


        Let's take your gist:



        "questionA": {
        "type": "object",
        "properties": {
        "answer": {
        "type": "string",
        "minLength": 1,
        "enum": ["Yes", "No"]
        }
        }
        }

        "questionB": {
        "type": "object",
        "properties": {
        "answer": {
        "type": null,
        }

        }
        }


        "questions": {
        "type": "object",
        "properties": {
        "A": {"$ref": "#/definitions/questionA"},
        "B": {"$ref": "#/definitions/questionB"}
        },
        "if": {
        "properties" : {
        "A": {"enum": ["Yes"]}
        }
        },
        "then": {
        "B": //Type = string and min length = 1 <-- Unsure what to put here to change the type of QuestionB
        }


        If I understand correctly your question, the effect you want to achieve is:



        If the respondent likes cars, ask him about favourite car and grab answer, else don't bother with the favourite car (and preferrably force the answer to be null).



        As Relequestual correctly ponted out in his comment, JSON Schema makes it hard to 'redefine' type. Moreover, each if-then-else content must be a valid schema on it's own.



        In order to achieve this effect, you may want to consider following approach:




        1. Define questionA as enum, as you did

        2. Leave property for questionB undefined upfront

        3. Define two possible schemas than can work as property definition for questionB and can be used as a result of dependency

        4. Make use od proper questionB definition in relation to value of questionA


        Some sample schema (draft07 compliant) which solves your case is listed below. Also some explanations provided below the schema.



        {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type" : "object",
        "propertyNames" : {
        "enum" : [
        "questionA",
        "questionB",
        ]
        },
        "properties" : {
        "questionA" : { "$ref" : "#/questionA" },
        "questionB" : { "$ref" : "#/questionB" },
        },
        "dependencies" : {
        "questionA" : {
        "$ref" : "#/definitions/valid-combinations-of-qA-qB"
        }
        },
        "definitions" : {
        "does-like-cars":{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["Yes","y"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:string...",
        "$ref" : "#/questionB/definitions/answer-def/string"
        }
        }
        }
        },
        "required" : ["questionB"]
        },
        "doesnt-like-cars" :{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["No","n"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:null...",
        "$ref" : "#/questionB/definitions/answer-def/null"
        }
        }
        }
        }
        },
        "valid-combinations-of-qA-qB" : {
        "anyOf" : [
        { "$ref" : "#/definitions/doesnt-like-cars" },
        { "$ref" : "#/definitions/does-like-cars" }
        ]
        },
        },
        "examples" : [
        {
        "questionA" : {
        "answer" : "Yes",
        },
        "questionB" : {
        "answer" : "Ass-kicking roadster",
        },
        },
        {
        "questionA" : {
        "answer" : "No",
        },
        "questionB" : {
        "answer" : null,
        },
        },
        {
        },
        ],
        "questionA" : {
        "$id" : "#/questionA",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "properties" : {
        "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
        },
        "definitions" : {
        "answer-def" : {
        "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
        "type" : "string",
        "enum" : ["Yes", "y", "No", "n"]
        }
        }
        },
        "questionB" : {
        "$id" : "#/questionB",
        "$comment" : "Please note no properties definitions here aside from propertyNames",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "definitions" : {
        "answer-def" : {
        "string" : {
        "type" : "string",
        "minLength" : 1,
        },
        "null" : {
        "type" : "null"
        }
        }
        }
        },
        }


        Why so complicated?



        Because your gist made it so ;-) And more seriously, this is because:




        • In your gist you define both questions as objects. There might be
          valid reason behind it, so I kept it that way (however whenever flat
          list of properties could be used, like "questionA-answer",
          "questionB-answer" I'd prefer it so to keep schema rules less nested,
          thus more readable and easy to create).


        • It seems from your question and gist, that this is important for you
          that "questionB/answer" is null instead of not being validated
          against/ignored when it's not relevant, thus I kept it so



        Step by step



        Questions as objects



        Please note, that I've created separate subschemas for "questionA" and "questionB". This is my personal preference and nothing stops you from getting everything inside "definitions" schema of main schema, however I do it usually that way because:




        • it's easier to split large schema into multiple smaller files after you make everything work like it should (encourages re-use of sub-schemas and helps to structur models in programming languages if someone gets idea to build their data model after my schema)

        • keeps object schemas/sub-schemas properly encapsulated and well, relative referencing is usually more clear to the reader as well

        • helps viewing complex schemas in editors that handle JSON syntax


        The "propertyNames"



        Since we're working here on "type" : "object" I used "propertyNames" keyword to define schema for allowed property names (since classess in programming languages usually have static sets of properties). Try to enter in each object a property outside of this set - schema valdiation fails. This prevents garbage in your objects. Should it be not desired behaviour, just remove "propertyNames" schemas from each object.



        "questionB" - where does the trick with changing type sits?



        The trick is: do not define property type and other relevant schema rules upfront. Please note how there's no "properties" schema in "questionB" schema. Instead, I used "definitions" to prepare two possible definitions of "answer" property inside "questionB" object. I will use them depending on "questionA" answer value.



        "examples" section?



        Some objects, that should illustrate how schema works. Play around with answer values, presence of properties etc. Please note, that an empty object will also pass validation, as no property is required (as in your gist) and there's only one dependency - if "questionA" appears, a "questionB" must show up as well.



        Ok, ok, top to bottom now, please



        Sure.
        So the main schema can have two properties:




        • questionA (an object containing property "answer")


        • questionB (an object containing property "answer")



        Is "#/questionA" required? -> No, at least based on your gist.



        Is "questionB" required? -> Only if "#/questionA" appears. To add insult to injury :-) the type and allowed values of "#/questionB/answer" strictly depend on the value of "#/questionA/answer".



        --> I can safely pre-define main object, foundation for questions objects and will need to define dependency



        {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type" : "object",
        "propertyNames" : {
        "enum" : [
        "questionA",
        "questionB",
        ]
        },
        "properties" : {
        "questionA" : { "$ref" : "#/questionA" },
        "questionB" : { "$ref" : "#/questionB" },
        },
        "dependencies" : {
        "questionA" : {
        "$comment" : "when questionA prop appears in validated entity, do something to enforce questionB to be what it wants to be! (like Lady Gaga in Machette...)"
        }
        },
        "questionA" : {
        "$id" : "#/questionA",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        },
        "questionB" : {
        "$id" : "#/questionB",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        },
        }


        Please note I am conciously setting relative base reference via "$id" keyword for question sub-schemas to be able to split schema into multiple smaller files and also for read-ability.



        --> I can safely pre-define "questionA/answer" property: type, allowed values etc.



        "questionA" : {
        "$id" : "#/questionA",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "properties" : {
        "answer" : {"$ref" : "#/questionA/definitions/answer-def"}
        },
        "definitions" : {
        "answer-def" : {
        "$comment" : "Consider using pattern instead of enum if case insensitiveness is required",
        "type" : "string",
        "enum" : ["Yes", "y", "No", "n"]
        }
        }
        },


        Note: I used "definitions" to, well, define schema for specific property. Just in case I'd need to re-use that definition somewhere else... (yep, paranoid about that I am)



        --> I can't safely pre-define "#/questionB/answer" property as mentioned above and must do the "trick" part in "#/questionB" sub-schema



        "questionB" : {
        "$id" : "#/questionB",
        "$comment" : "Please note no properties definitions here aside from propertyNames",
        "type" : "object",
        "propertyNames" : {
        "enum" : ["answer"]
        },
        "definitions" : {
        "answer-def" : {
        "string" : {
        "type" : "string",
        "minLength" : 1,
        },
        "null" : {
        "type" : "null"
        }
        }
        }
        },


        NOTE: See "#/definitions/answer-def"? There are two sub-nodes to that, "#/definitions/answer-def/string" and "#/definitions/answer-def/null" . I wasn't entirely sure how I'll do it at the moment, yet I knew I definitely will need that capability of juggle with "#/questionB/answer" property schema in the end.



        --> I must define rules for valid combinations of both answers and since "#/questionB/answer" must be always present; I'm doing that in main schema, which uses questions sub-schemas as it's a cap over them that logically makes a good place to define such rule.



        "definitions" : {
        "does-like-cars":{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["Yes","y"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:string...",
        "$ref" : "#/questionB/definitions/answer-def/string"
        }
        }
        }
        },
        "required" : ["questionB"]
        },
        "doesnt-like-cars" :{
        "properties" : {
        "questionA" : {
        "properties" : {
        "answer" : { "enum" : ["No","n"] }
        }
        },
        "questionB" : {
        "properties" : {
        "answer" : {
        "$comment" : "Here #/questionB/answer becomes a type:null...",
        "$ref" : "#/questionB/definitions/answer-def/null"
        }
        }
        }
        }
        },
        "valid-combinations-of-qA-qB" : {
        "anyOf" : [
        { "$ref" : "#/definitions/doesnt-like-cars" },
        { "$ref" : "#/definitions/does-like-cars" }
        ]
        },


        So there are those, who like cars - I basically define allowed values of "#/questionA/answer" and relevant definition of property of "#/questionB/answer". Since this is the schema, both sets must match to fulfill this definition. Please note I marked "questionB" property key as required in order to not validate JSON that contains only "questionA" property key against schema.



        I did similar thing for those, who don't like cars (how one cannot like cars?! Wicked times...) and at the end I said in "valid-combinations-of-qA-qB": It's either or, people. Either you like cars and give me the answer or you don't like cars and the answer must be null. "XOR" ("oneOf") comes to mind automatically but since I've defined like cars AND answer and doesn't like cars AND answer = null as a complete schemas, logical OR is completely sufficient -> "anyOf".



        At the end the finishing touch was to use that rule in "dependencies" section of main schema, which translates to: if "questionA" appears in validated instance, either... or...



        "dependencies" : {
        "questionA" : {
        "$ref" : "#/definitions/valid-combinations-of-qA-qB"
        }
        },


        Hope it clarifies and helps with your case.



        Open questions



        Why not use object "answers" with properties reflecting each question answer, with key identifying the question? It could simplify a bit rules and references with regards to dependencies between answers (less typing, yep, I'm a lazy lad).



        Why "#/questionB/answer" must be null instead just ignoring it if "#/questionA/answer" : { "enum" : ["No"] } ?



        Recommended reading



        See "Understanding JSON Schema" : https://json-schema.org/understanding-json-schema/index.html



        Some basic examples: https://json-schema.org/learn/



        JSON schema validation reference: https://json-schema.org/latest/json-schema-validation.html



        A lot of StackOverflow Q&A provides nice insight in how to manage different cases with JSON Schema.



        Also it might be helpful at occasion to check for relative JSON Pointers RFC.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 2 days ago

























        answered Nov 14 at 22:45









        PsychoFish

        75689




        75689






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53206547%2fjson-schema-property-dependent-on-value-of-previous-property%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown