JSON Schema to represent a name and value with value constrained by name - jsonschema

I have the following JSON snippets which are all valid
"units": { "name": "EU", "value": "Grams" }
"units": { "name": "EU", "value": "Kilograms" }
"units": { "name": "US", "value": "Ounces" }
"units": { "name": "US", "value": "Pounds" }
The name values can be EU and US and the valid value value should depend on the name value.
It's easy to use JSON Schema enums for both these properties, but can I enforce the additional constraint using JSON Schema?
I would consider changing the overall schema so that there is a parent child relationship between a name object and value object, but ideally this would be avoided.

I managed to crack it using https://www.jsonschemavalidator.net/ to work though an example. The following schema provides the solution:
"units": {
"type":"object",
"oneOf": [ {
"properties": {
"name": { "enum": [ "EU" ] },
"value": { "enum" : ["Grams", "Kilograms"]}}}, {
"properties": {
"name": { "enum": [ "US" ] },
"value": { "enum": ["Ounces", "Pounds"]}}}]
}

Related

json-schema : Get a list of additionalProperties?

Is it possible to get a list of all additionalProperties found by json-schema ?
For example, if my schema looks like this :
{
"type": "object",
"properties": {
"firstName": {
"type": "string",
},
"lastName": {
"type": "string",
},
"age": {
"type": "integer"
}
}
}
And data loooks like this :
{
"firstName": "John",
"lastName": "Doe",
"age": 21,
"extraField": "some new data I was not expecting",
"anotherExtraField": "another unexpected data point"
}
In this case, instead of an exception from json-schema because of additionalProperties: false, I want a list in return, like : [extraField, anotherExtraField]
If you're using an implementation that supports 2019-09 or 2020-12 with annotations, you're in luck! additionalProperties should produce an annotation result of the properties it validates (spec).
If you add additionalProperties: true, then all extra properties pass and are validated by the keyword, which means those extra properties should be listed in the annotation result.
{
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"additionalProperties": true
}
This yields (in the Detailed output format)
{
"valid": true,
"keywordLocation": "#",
"instanceLocation": "#",
"annotations": [
{
"valid": true,
"keywordLocation": "#/properties",
"instanceLocation": "#",
"annotation": [
"firstName",
"lastName",
"age"
]
},
{
"valid": true,
"keywordLocation": "#/additionalProperties",
"instanceLocation": "#",
"annotation": [
"extraField",
"anotherExtraField"
]
}
]
}
You can try it on https://json-everything.net, which is powered by my validator, JsonSchema.Net.
If you're not using .Net, you can browse the implementations page for other libraries. Some of them may also support annotations, but I'm not sure which do.

Is it possible to be agnostic on the properties' names?

Let's say I want to have a schema for characters from a superhero comics. I want the schema to validate json objects like this one:
{
"Name": "Roberta",
"Age": 15,
"Abilities": {
"Super_Strength": {
"Cost": 10,
"Effect": "+5 to Strength"
}
}
}
My idea is to do it like that:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "characters_schema.json",
"title": "Characters",
"description": "One of the characters for my game",
"type": "object",
"properties": {
"Name": {
"type": "string"
},
"Age": {
"type": "integer"
},
"Abilities": {
"description": "what the character can do",
"type": "object"
}
},
"required": ["Name", "Age"]
}
And use a second schema for abilities:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "abilities_schema.json",
"title": "Abilities",
"type": "object",
"properties": {
"Cost": {
"description": "how much mana the ability costs",
"type": "integer"
},
"Effect": {
"type": "string"
}
}
}
But I can't figure how to merge Abilities in Characters. I could easily tweak the schema so that it validates characters formatted like:
{
"Name": "Roberta",
"Age": 15,
"Abilities": [
{
"Name": "Super_Strength"
"Cost": 10,
"Effect": "+5 to Strength"
}
]
}
But as I need the name of the ability to be used as a key I don't know what to do.
You need to use the additionalProperties keyword.
The behavior of this keyword depends on the presence and annotation
results of "properties" and "patternProperties" within the same schema
object. Validation with "additionalProperties" applies only to the
child values of instance names that do not appear in the annotation
results of either "properties" or "patternProperties".
https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.3.2.3
In laymans terms, if you don't define properties or patternProperties the schema value of additionalProperties is applied to all values in the object at that instance location.
Often additionalProperties is only given a true or false value, but rememeber, booleans are valid schema values.
If you have constraints on the keys for the object, you may wish to use patternPoperties followed by additionalProperties: false.

Json schema conditional validation configuration - Unsupported keyword(s): ["const"]]

I want to set up the conditional validation in my schema. I saw an example here on SO.
I have a similar setup, where I would like to validate if the field public is set to string "public". If it is set to "public" then I want to make fields description, attachmentUrl and tags required. If the field is not set to "public" then this fields are not required.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Update todo",
"type": "object",
"properties": {
"public": {
"type": "string"
},
"description": {
"type": "string",
"minLength": 3
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true,
"minItems": 1
},
"attachmentUrl": {
"type": "string"
}
},
"anyOf": [
{
"not": {
"properties": {
"public": { "const": "public" }
},
"required": ["public"]
}
},
{ "required": ["description", "tags", "attachmentUrl"] }
],
"additionalProperties": false
}
But, when I try to deploy it like that, I get the following error:
Invalid model specified: Validation Result: warnings : [], errors :
[Invalid model schema specified. Unsupported keyword(s): ["const"]]
The "const" keyword wasn't added until draft 06. You should upgrade to an implementation that supports at least that version.
https://json-schema.org/draft-06/json-schema-release-notes.html#additions-and-backwards-compatible-changes
Otherwise, you can use "enum" with a single value: "enum": ["public"]

How do I indicate which "oneOf" API response will use?

I have an API where the basic response of one key will have an array of identifiers. A user may pass an extra parameter so the array will turn to an array of objects from an array of strings (for actual details rather than having to make a separate call).
"children": {
"type": "array",
"items": {
"oneOf": [{
"type": "string",
"description": "Identifier of child"
}, {
"type": "object",
"description": "Contains details about the child"
}]
}
},
Is there a way to indicate that the first type comes by a default and the second via a requested param?
It's not entirely clear to me what you are trying to accomplish with the distinction. Really that sounds like documentation; maybe elaborate in the descriptions of each oneOf subschema.
You could add an additional boolean field at the top level (sibling of children) to indicate whether detailed responses are returned and provide a default value for that field. The next step is to couple the value of the boolean to the type of the array items, which I've done using oneOf.
I'm suggesting something along the lines of:
{
"children": {
"type": "array",
"items": {
"oneOf": [
{
"type": "string",
"description": "Identifier of child",
"pattern": "^([A-Z0-9]-?){4}$"
},
{
"type": "object",
"description": "Contains details about the child",
"properties": {
"age": {
"type": "number"
}
}
}
]
}
},
"detailed": {
"type": "boolean",
"description": "If true, children array contains extra details.",
"default": false
},
"oneOf": [
{
"detailed": {
"enum": [
true
]
},
"children": {
"type": "array",
"items": {
"type": "object"
}
}
},
{
"detailed": {
"enum": [
false
]
},
"children": {
"type": "array",
"items": {
"type": "string"
}
}
}
]
}
The second oneOf places a further requirement on the response object that when "detailed": true the type of items of the "children" array must be "object". This refines the first oneOf restriction that describes the schema of objects in the "children" array.

Json schema variable properties

Based some condition my IPV4 can have either 2 or 3 properties but those are required. How to define it. I tried below schema. I get error saying "JSON is valid against more than one schema from 'oneOf'. Valid schema indexes: 0, 1"
"IPv4Type": {
"type": "object",
"oneOf": [
{
"properties": {
"provider-address": {
"type": "string",
"format": "ipv4"
},
"customer-address": {
"type": "string",
"format": "ipv4"
},
"mask": {
"type": "number"
}
},
"required": [
"provider-address",
"customer-address",
"mask"
]
},
{
"properties": {
"provider-address": {
"type": "string",
"format": "ipv4"
},
"mask": {
"type": "number"
}
},
"required": [
"provider-address",
"mask"
]
}
]
}
A few ideas:
you can drop the oneOf , define your JSON in one object, with all 3 properties defined, but adding only "provider-address" and "mask" as required
defining "additionalProperties": false the the 2nd definition under oneOf
replacing oneOf with anyOf