How would you design JSON Schema for an arbitrary key? - json

I have the following JSON output data:
{
"label_name_0" : 0,
"label_name_5" : 3,
.
.
.
"label_name_XXX" : 4
}
The output is simple: a key[1] name associated with integer value. If the key name doesn't change, I can easily come up with JSON Schema similar to this:
{
"type": "array"
"title": "Data output",
"items" :{
"properties": {
"label_name": {
"type": "integer",
"default": 0,
"readonly": True,
}
}
},
Since the key name itself is not known and keep changing, I have to design schema for it. The only thing I know is that the key is string and not more than 100 characters. How do I define a JSON Schema for the key lable_name_xxx that keeps changing.
[1] Not sure if I am using the right terminology

On json-schema.org you will find something appropriate in the File System Example section. You can define patternProperties inside an object.
{
"type": "object",
"properties": {
"/": {}
},
"patternProperties": {
"^(label_name_[0-9]+)+$": { "type": "integer" }
},
"additionalProperties": false,
}
The regular expression (label_name_[0-9]+)+ should fit your needs. In JSON Schema regular expressions are explicitly anchored with ^ and $. The regular expressions defines, that there has to be at least one property (+). The property consists of label_name_ and a number between 0 and 9 whereas there has to be at least one number ([0-9]+), but there can also arbitrary many of them.
By setting additionalProperties to false it constrains object properties to match the regular expression.

As Konrad's answer stated, use patternProperties. But use in place of properties, which is not needed, and I think Konrad just pasted from his reference example that was expecting a path starting with /. In the example below, the pattern match regex .* accepts any property name and I am allowing types of string or null only by using "additionalProperties": false.
"patternProperties": {
"^.*$": {
"anyOf": [
{"type": "string"},
{"type": "null"}
]
}
},
"additionalProperties": false

Simpler solution than patternProperties, since OP does not have any requirement on the key names (documentation):
{
"type": "object",
"additionalProperties": {
"type": "integer",
"default": 0,
"readonly": true,
}
}
default and readonly included because they were included in the OP's initial suggestion, but they are not required.

Related

JSON Schema construction for a objects with. common properties but differing in some properties

I have a a number of objects which share a common set of features but differ in one or more properties. The common content is specified as media content in the definitions. I have provided one such object with a 'format' property, but there are other objects, omitted to keep it short, that also have additional properties. Here is a snippet of my attempt at constructing the schema. Is this the correct way to accomplish, this? Many thanks
"definitions": {
"media-content":{
"type": "object",
"title": {
"type": "string"
},
"related-media": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"type": "object",
"properties": {
"type": {
"format": "string",
"enum":["audio", "video"]
},
"category": {
"$ref": "#/definitions/media-content"
}
}
Is this the way to do it?
The first thing that stands out to me is that this isn't valid JSON Schema.
The title keyword provides a title for the schema so it expects a string, but because you've provided a schema, it looks like you're wanting it to be a property. Similarly related-media looks like you expect this to be a property. Are you missing wrapping these in a properties keyword, like you have later for type and category?
These changes would make media-content look like this:
"media-content":{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"related-media": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
I have provided one such object with a 'format' property
Again, here, I'm not sure what you're getting at.
"properties": {
"type": {
"format": "string",
"enum":["audio", "video"]
},
"category": {
"$ref": "#/definitions/media-content"
}
}
This says you're expecting type to be a property in your object, but format isn't used right. Although the format keyword has some predefined types and does accept custom values, the way you're using it look like you really want to be using the type keyword. In the end, it doesn't matter because enum will restrict the value to the items you declare in the array ("audio" or "video").
It might be easier to see what you're trying to do if you posted a full minimum exaple.
That said, I'll try to build one to answer the question I think you're asking.
It sounds like you're wanting to build a polymorphic relationship: an inheritance hierarchy where a base type defines some properties and a number of derived types define additional properties.
There's not really a good way to do that with JSON Schema because JSON Schema is a constraints system. That is, you start with {} where anything is valid, and by adding keywords, you're reducing the number of values that are valid.
Still, you might be able to achieve something close by using allOf and $ref.
First, declare the base property set in its own schema. I'd separate them into independent schemas for easier handling. You also need to give it an $id.
{
"$id": "/base-type"
"type": "object",
"properties": {
"base-prop-1": { "type": "string" },
"base-prop-2": { "type": "number" }
}
}
Next, for each of your "derived" schemas, you'll want to create a new schema, each with their own $id value, that references the base schema and declares its own additional requirements.
{
"$id": "/derived-type-1",
"allOf": [
{ "$ref": "/base-type" },
{
"properties": {
"derived-prop": { "type": "boolean" }
}
}
]
}
This second schema requires everything from the /base-type and also requires a derived-prop property that holds a boolean value.

Apply required field to referenced JSON data schema

I have the following use-case I try to solve with JSON schemas.
I have a generic JSON data schema for, for example, a user. Here is an example of the user.schema.json file.
{
"type": "object",
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"email": {
"type": "string",
"minLength": 1
},
"locale": {
"type": "string",
"minLength": 1
},
"active": {
"type": "boolean",
"default": true
},
"password": {
"type": "string",
"minLength": 8
},
"roles": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
}
}
Now I have 2 different kinds of requests:
- POST: Add a user
- PATCH: Update user data.
In 1 case, I can send this data structure, with 3 required fields, while in case of a patch each field is optional.
So I get the post request file: post-user.schema.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "user.schema.json",
"required": [
"name",
"password",
"email"
]
}
And for my patch (path-user.schema.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "user.schema.json"
}
Now the issue that I am having is that my POST schema also marks a user like:
{
"name": "NoPassword",
"email": "nopassword#moba.nl",
"roles": []
}
Which is missing the required password field, as a valid JSON schema.
Apparently, this is not the way to assign required fields to a referenced data structure. I have tried to use google to see what I can find on the subject regarding this using searches like:
[ how to assign required field to referenced schema's ]
and I tried to obtain this info from the documentation.
I have no luck.
My questions now are:
A. Is it possible to assign required fields to a $referenced json schema data object.
B. If this is possible how to do it
C. If this is not possible, what would be a good way to approach this.
Any help is much appreciated.
Using $ref results in all other properties in the object being ignored, so you need to wrap your use of $ref.
Let's take a look at the spec:
An object schema with a "$ref" property MUST be interpreted as a
"$ref" reference. The value of the "$ref" property MUST be a URI
Reference. Resolved against the current URI base, it identifies the
URI of a schema to use. All other properties in a "$ref" object MUST
be ignored.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-01#section-8.3
Then consider the schema you included in your question:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "user.schema.json",
"required": [
"name",
"password",
"email"
]
}
Reading the spec, you can see why required will be ignored.
Originally $ref was only designed to replace a WHOLE object, not ADD to the conditions for the object.
What you want is for multiple schemas to be applied to the instance. To do this, you use allOf.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"allOf": [
{
"$ref": "user.schema.json"
},
{
"required": [
"name",
"password",
"email"
]
}
]
}
I loaded this schema into a demo for you to test at https://jsonschema.dev - although it doesn't support references yet, so I transcluded the reference, but the validation will work the same.
From draft-8 onwards, $ref will behave as you expect, as it becomes an applicator keyword rather than a keyword with special behaviours, meaning other keywords in the same object will not need to be ignored.

JSON schema - support regular expression for field

I want to use the following library gojsonschema to validate json structure,
Currenlty I've two questions to the example below
https://github.com/xeipuuv/gojsonschema
what is the #/definitions/.... and what is the purpose of it?
the name should have the following:
{required: true, unique: true, pattern: '/^[A-Za-z0-9_\-\.]+$/'}
The unique is that if I've another name filed in the schema it should be unique, How can I validate it with jsonschema?
var schema = `
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"name": ...
"properties": {
"username": {"$ref": "#/definitions/name"},
update
what I found is this
https://spacetelescope.github.io/understanding-json-schema/reference/regular_expressions.html
But how should I check it inside the json i've provided
should It be like this
var schema = `
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"name": {
"type": "object",
"pattern":'/^[A-Za-z0-9_\-\.]+$/'
}
"properties": {
"username": {"$ref": "#/definitions/name"},
https://datatracker.ietf.org/doc/draft-handrews-json-schema-validation/?include_text=1
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"username": {
"type": "array",
"items": {
"type": "string",
"pattern": "/^[A-Za-z0-9_\-\.]+$/"
},
"uniqueItems": true
}
},
"required": [
"username"
]
}
1: The "definitions" keywords provides a standardized location for
schema authors to inline re-usable JSON Schemas into a more general
schema. The keyword does not directly affect the validation result.
This keyword's value MUST be an object. Each member value of this
object MUST be a valid JSON Schema.
As an example, here is a schema describing an array of positive
integers, where the positive integer constraint is a subschema in
"definitions":
{
"type": "array",
"items": { "$ref": "#/definitions/positiveInteger" },
"definitions": {
"positiveInteger": {
"type": "integer",
"exclusiveMinimum": 0
}
}
}
2: uniqueItems
The value of this keyword MUST be a boolean.
If this keyword has boolean value false, the instance validates
successfully. If it has boolean value true, the instance validates
successfully if all of its elements are unique.
Omitting this keyword has the same behavior as a value of false.
required
The value of this keyword MUST be an array. Elements of this array,
if any, MUST be strings, and MUST be unique.
An object instance is valid against this keyword if every item in the
array is the name of a property in the instance.
Omitting this keyword has the same behavior as an empty array.
properties
{
"type": "object",
"properties": {
"progBinaryName": {
"type": "string",
"pattern": "^[A-Za-z0-9 -_]+_Prog\\.(exe|EXE)$"
}
}
}
The value of "properties" MUST be an object. Each value of this
object MUST be a valid JSON Schema.
This keyword determines how child instances validate for objects, and
does not directly validate the immediate instance itself.
Validation succeeds if, for each name that appears in both the
instance and as a name within this keyword's value, the child
instance for that name successfully validates against the
corresponding schema.
Omitting this keyword has the same behavior as an empty object.
#/definitions/name is an internal reference to another schema. The document should have section that looks like the following that is being referenced:
"definitions": {
"name": {
...
}
}
JSON schema does not have a unique validator. There is a uniqueItems validator that can be used with arrays but I don't think that is what you are looking for.

Creating a type definition for a property named "type" using JSON schema

I'm trying to create a JSON schema for an existing JSON file that looks something like this:
{
"variable": {
"name": "age",
"type": "integer"
}
}
In the schema, I want to ensure the type property has the value string or integer:
{
"variable": {
"name": "string",
"type": {
"type": "string",
"enum": ["string", "integer"]
}
}
}
Unfortunately it blows up with message: ValidationError {is not any of [subschema 0]....
I've read that there are "no reserved words" in JSON schema, so I assume a type of type is valid, assuming I declare it correctly?
The accepted answer from jruizaranguren doesn't actually answer the question.
The problem is that given JSON (not JSON schema, JSON data) that has a field named "type", it's hard to write a JSON schema that doesn't choke.
Imagine that you have an existing JSON data feed (data, not schema) that contains:
"ids": [ { "type": "SSN", "value": "123-45-6789" },
{ "type": "pay", "value": "8675309" } ]
What I've found in trying to work through the same problem is that instead of putting
"properties": {
"type": { <======= validation chokes on this
"type": "string"
}
you can put
"patternProperties": {
"^type$": {
"type": "string"
}
but I'm still working through how to mark it as a required field. It may not be possible.
I think, based on looking at the "schema" in the original question, that JSON schemas have evolved quite a lot since then - but this is still a problem. There may be a better solution.
According to the specification, in the Valid typessection for type:
The value of this keyword MUST be either a string or an array. If it is an array, elements of the array MUST be strings and MUST be unique.
String values MUST be one of the seven primitive types defined by the core specification.
Later, in Conditions for successful validation:
An instance matches successfully if its primitive type is one of the types defined by keyword. Recall: "number" includes "integer".
In your case:
{
"variable": {
"name": "string",
"type": ["string", "integer"]
}
}

Enforcing "style" rules in JSON Schema files?

I am looking at using JSON Schemas for an upcoming project, and looking for a way to validate our naming conventions/style and consistency rules in the JSON Schema file. Somewhat similar to StyleCop or Checkstyle.
Using this samples from JSON Schema Lint to illustrate:
{
"description": "Any validation failures are shown in the right-hand Messages pane.",
"type": "object",
"properties": {
"foo": {
"type": "number"
},
"bar": {
"type": "string",
"enum": [
"a",
"b",
"c"
]
}
}
}
Imagine another developer wants to add a new property, but I want to prevent property names from being upper-case (baz instead of Baz) or maybe boolean properties should start with "is" (isBaz). Is there a way to "unit test" the JSON Schema file and check for that?
"Baz": {
"type": "boolean"
},
It feels like a custom validator for the JSON Schema file (vs. using the JSON Schema to validate the JSON output). Does something like that already exist, or do I just parse the JSON schema file myself and write the rules?
It's completely possible to write a meta-schema that enforces this constraint on your schemas. Let's construct it step-by-step:
1. Constraining property names
The key part is to use patternProperties to specify which property names are allowed, and additionalProperties to disallow anything else:
{
"patternProperties": {
"^[a-z]+([A-Z][a-z]*)*$": {}
},
"additionalProperties": false
}
(For this example, I've used the regex ^[a-z]+([A-Z][a-z]*)*$ to detect alphabetic-only lowerCamelCase)
Note that it doesn't matter whether provide any constraints for suitably-named properties (here it's just the empty schema {}). However, the presence of this definition means that any matching property is allowed, while anything else is banned by additionalProperties.
Fancier constraints
For other constraints (such as your "boolean properties must start with is" one), you just add more complex entries here.
This answer focuses more on how to make a generic recursive naming-style schema. It's already pretty long, so if you're looking for guidance on how to express a specific constraint, then it might be neater to ask as a separate question.
2. Applying to the properties property
This bit's pretty simple - make these constraints apply to the appropriate part of the schema:
{
"properties": {
"properties": {"$ref": "#/definitions/propertyStyleRule"}
},
"definitions": {
"propertyStyleRule": {
"patternProperties": {
"^[a-z]+([A-Z][a-z]*)*$": {}
},
"additionalProperties": false
}
}
}
3. Make it recursive
In fact, you don't just want to cover sub-schemas inside "properties", but also "items", "anyOf", etc.
Here it gets quite long, so I'll omit most of it, but basically you go through every keyword that might contain a schema, and make sure they are subject to the same naming-scheme by referencing the root schema:
{
"properties": {
"properties": {"$ref": "#/definitions/propertyStyleRule"},
"additionalProperties": {"$ref": "#"},
"items": {"$ref": "#"},
"not": {"$ref": "#"},
"allOf": {"$ref": "#"},
...
},
"definitions": {
"propertyStyleRule": {
"patternProperties": {
"^[a-z]+([A-Z][a-z]*)*$": {"$ref": "#"}
},
"additionalProperties": false
}
}
}
Note: we've also now replaced the empty schema ({}) in our "propertyStyleRule" definition with a reference back to the root ({"$ref": "#"}), so the sub-schemas inside properties also recurse properly.
4. Hang on, some of those keywords can be arrays, or booleans, or...
OK, so there's an obvious problem here: "not" holds a schema, so that's fine, but "allOf" holds an array of schemas, "items" can hold either, and "additionalProperties" can be a boolean.
We could do some fancy switching with different types, or we could simply add an items entry to our root schema:
{
"items": {"$ref": "#"},
"properties": {
...
},
"definitions": {
"propertyStyleRule": {...}
}
}
Because we haven't specified a type, our root schema actually allows instances to be objects/arrays/boolean/string/whatever - and if the instance isn't an object, then properties is just ignored.
Similarly, items is ignored unless the instance is an array - but if it is an array, then the entries must also follow the root schema. So it doesn't matter whether the value of "items" is a schema or an array of schemas, it recurses properly either way.
5. Schema maps
For a few keywords (like "patternProperties" or "definitions") the value is not a schema, it's a map of strings to schemas, so you can't just reference the root schema. For these, we'll make a definition "schemaMap", and reference that instead:
{
"items": {"$ref": "#"},
"properties": {
"properties": {"$ref": "#/definitions/propertyStyleRule"},
"additionalProperties": {"$ref": "#"},
"items": {"$ref": "#"},
"not": {"$ref": "#"},
"allOf": {"$ref": "#"},
...
"patternProperties": {"$ref": "#/definitions/schemaMap"},
...
},
"definitions": {
"schemaMap": {
"type": "object",
"additionalProperties": {"$ref": "#"}
},
"propertyStyleRule": {...}
}
}
... and you're done!
I've left out details, but hopefully it's clear enough how to write the full version.
Also, once you've written this once, it should be pretty easy to adapt it for different style rules, or even applying similar constraints to the names in "definitions", etc. If you do write a schema like this, please consider posting it somewhere so that other people can adapt it! :)