I have a json object like:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
The sdk name
can either be android or ios
. And the session_id
is based on name field
in sdk json
. I have written a json schema
using conditional statement (Using draft 7) as follows:
But it works in an unexpected manner:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/Base",
"definitions": {
"Base": {
"type": "object",
"additionalProperties": false,
"properties": {
"session": {
"$ref": "#/definitions/Session"
},
"sdk": {
"$ref": "#/definitions/SDK"
}
},
"title": "Base"
},
"Session": {
"type": "object",
"additionalProperties": false,
"properties": {
"start_timestamp": {
"type": "integer",
"minimum": 0
},
"session_id": {
"type": "string",
"if": {
"SDK": {
"properties": {
"name": {
"enum": "ios"
}
}
}
},
"then": {
"pattern": "A"
},
"else": {
"pattern": "B"
}
}
},
"required": [
"session_id",
"start_timestamp"
],
"title": "Session"
},
"SDK": {
"type": "object",
"additionalProperties": false,
"properties": {
"version": {
"type": "string"
},
"name": {
"type": "string",
"enum": [
"ios",
"android"
]
}
},
"required": [
"name",
"version"
],
"title": "SDK"
}
}
}
So the following JSON Passes:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "ios",
"version": "21"
}
}
But this fails:
{
"session": {
"session_id": "B",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
can someone explain y?.. Even this passes:
{
"session": {
"session_id": "A",
"start_timestamp": 1535619633301
},
"sdk": {
"name": "android",
"version": "21"
}
}
I think you're having a similar problem as in this question.
@Relequestual is right in that you need the
properties
keyword around yourSDK
callout. But for what you want to do, you need to reorganize.Subschemas only operate on their level in the instance, not at the root.
Consider this schema for a simple JSON object instance containing a
one
and atwo
property:The
if
keyword under thetwo
property can only consider the portion of the instance under thetwo
property (i.e.two
's value). It's not looking at the root of the instance, so it can't see theone
property at all.To make it so that the subschema under the
two
property subschema can see theone
property in the instance, you have to move theif
outside of theproperties
keyword.For two possible values of
one
, this is pretty good. Even three possible values isn't bad. However, as the possible values ofone
increases, so does the nesting ofif
s, which can make your schema horrible to read (and possibly make validation slower).Instead of using the
if
/then
/else
construct, I suggest using ananyOf
oroneOf
where each subschema represents a valid state for the instance, given the varying values ofone
.This is much cleaner in my opinion.
Hopefully that explanation helps you reconstruct your schema to allow those other instances to pass.
You have to move your conditional to a high enough level to be able to reference all of the the properties it needs to reference. In this case, that's the
/definitions/Base
schema. Then you just need to write your schemas properly as Relequestual explained.The value of
if
must be a JSON Schema. If you were to take lines https://gist.github.com/Relequestual/f225c34f6becba09a2bcaa66205f47f3#file-schema-json-L29-L35 (29-35) and use that as a JSON Schema by itself, you would impose no validation constraints, because there are no JSON Schema key words at the top level of the object.This is allowed in the specification, because people may want to extend the functionality of JSON Schema by adding their own key words. So it's "valid" JSON Schema, but doesn't actually DO anything.
You Need to add
properties
to the schema for it to make sense.Additionally,
enum
must be an array. When you only have a single item, you may useconst
.