How to make JSON Schema recognize a hierarchy of objects? - json

I'm writing a schema for JSON. Let's say I have an object called "feature". Inside this object there are various properties defined, one of which is "features", which is an array containing another set of "feature". So the hierarchy is virtually infinite.
{
"name": "feature",
"properties": {
"feature_name": {
"type": "string",
"description": "Name of feature",
"required": true
},
"feature_type": {
"type": "string",
"description": "Type of feature",
"required": true
},
"features": {
"type": "array",
"items": {
//How do I define the same object for an infinite hierarchy?
}
}
}
}
The "features" array could consist of many more "feature" objects, each of which may also have more "features". Can I accommodate for this in a JSON schema?

You are probably not going to be able to implement an infinite hierarchy. JSON.net seems to be one of the better solutions for this type of problem.

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.

Validate each JSON node with different JSON schema

Im trying to make a system monitor, which is highly customisable by user. This customization is achieved by using JSON file for modeling look of system monitor. The JSON could look like this.
{
"_": "WINDOW",
"name": "myWindow",
"children": [
{
"_": "CPU",
"name": "cpuMonitor",
"freq_Unit": "MHZ"
},
{
"_": "NETWORK",
"name": "network",
"unit": "Kb/s"
},
{
"_": "DISK",
"name": "disk"
}
],
"background": "red"
}
As you can see, each object coresponds to this schema.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"name":"Component",
"type": "object",
"properties":{
"_": {
"type": "string"
},
"name":{
"type":"string"
},
"childern":{
"type":"array"
}
},
"required": ["_","name"]
}
But each component has also its own schema definition. I'd like to parse whole JSON and validate each node for different schema (first if its component and then to its corresponding schema).
I had look at rapidJson and other libraries, but I didnt find solution for validating nodes for different schema. Do you know any library which could do that? Or is it even possible to validate JSON in this way?
All feedback on how to solve this will be appreciated.
Edit: Corrected schema :(
There's a simple approach involved with that, use the oneOf pattern declaration to specify the layout of the array elements. Inside these nested declarations, you specify the fixed identifier (probably the content of your _ field) as a constant, so that there is only one nested schema matching each of your panel types.
Notes:
I had to specify the constant type identifier using the enum specifier because the regular constant specifier didn't work with the library I was using. This may also have been an oversight in the revision of the specification that it was based on.
A different approach is to split the the validation steps. You simply verify that the elements of the array are objects and that they have a string field _ containing one of the supported types. When iterating over the array, you then validate each field individually according to its _ field.
In addition to Ulrich's answer, here's an example of what I'd do:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Component",
"type": "object",
"definitions": {
"base": {
"properties": {
"name": { "type": "string" },
"children": {
"type": "array",
"items": { "$ref": "#" }
}
},
"required": [ "_", "name" ]
},
"cpu": {
"properties": {
"_": { "const": "CPU" },
"freq_Unit": "MHZ"
}
},
"network": {
"properties": {
"_": { "const": "NETWORK" },
"unit": "Kb/s"
}
},
"disk": {
"properties": {
"_": { "const": "DISK" }
}
},
"window": {
"properties": {
"_": { "const": "WINDOW" },
"background": { "enum": [ "red", "orange", "yellow", ... ] }
}
}
},
"allOf": [
{ "$ref": "#/definitions/base" },
{
"oneOf": [
{ "$ref": "#/definitions/cpu" },
{ "$ref": "#/definitions/network" },
{ "$ref": "#/definitions/disk" },
{ "$ref": "#/definitions/window" }
]
}
]
}
First, we require that any instance MUST adhere to base which declares _ and name as required properties. Additionally, we declare a children array property that requires all items also match this schema (giving us a recursive behavior). This doesn't really do much except that it allows us to declare these things in one place instead of having to declare them in the other three definitions.
(Note that we don't declare _ in the properties list. This means that any value will pass for this portion of the schema. We clean it up in the next part. If you want to ensure that future components are declared with strings, then you can add a "type": "string" requirement to that property, but I don't feel it's necessary unless others are authoring those components.)
Second, we declare each of our specific types as separate definitions, using the const keyword to isolate the one we want. This construct is analogous to a switch (or case) statement. If the instance doesn't match one of these explicit options, it fails. If it's missing one of the required base properties, it fails.
This will get you where you want to be.
To take it further, there are two more things you can do:
Add required to the other definitions to say that the specific properties are also required (e.g. freq_Unit for the cpu definition).
Declare each of the definitions in separate files. This would allow you to add a new definition by simply adding a new file and referencing it in the main schema. In my opinion, it's a bit cleaner. Some people prefer to have it all in one file, though.

Multiple schema JSON validation in Golang

I need to validate several JSON files against a schema in Golang.
I have been able to achieve it by using gojsonschema, that is really a straight forward library.
However, the problem I'm facing right now is that I have been given with schemas that have dependencies to another schemas and haven't found the way to load all the schemas that I need. Therefore, my validations always fail.
This is my main schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$ref": "#/definitions/List",
"definitions": {
"List": {
"type": "array",
"items": {
"$ref": "#/definitions/Item"
}
},
"Item": {
"description": "An item ....",
"type": "object",
"additionalProperties": false,
"properties": {
"property01": {
"description": "The property01 code.",
"$ref": "./CommonTypes.json#/definitions/Type01Definition"
}
},
"required": [
"property01"
]
}
}
}
And, I have another one with common types:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"Type01Definition": {
"description": "The definition for the type 01",
"type": "string",
"pattern": "^[A-Z0-9]{3}$"
}
}
}
Is there a way to load several schemas using that library? Or is there any other Golang library that allows to achieve that?
The way to refer to a file using $ref is to specify the absolute path of the file using a URL scheme. If you change the $ref to look like "$ref" : "file:///home/user/directory/CommonTypes.json#/definitions/Type01Definition, your example will work as expected.
If you need a bit more flexibility you can either try gojsonschema's NewReferenceLoaderFilesystem or switch to a different Golang library https://github.com/santhosh-tekuri/jsonschema. That library allows you to add custom resources so you can load several schemas at once.

JSON schema enum vs pattern for single value

I have an sample json:
{
"type": "persons",
"id": 2,
"attributes": {
"first": "something",
"second": "something else"
}
}
And I have to make a schema for it (using JSON API specs and JSON schema docs):
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"type": {
"type": "string",
"pattern": "^persons$"
},
"id": {
"type": "integer"
},
"attributes": {
"type": "object",
"properties": {...}
}
},
"required": ["type", "id", "attributes"]
}
And the question is: if the only acceptable value for "type" is "persons", should I use in schema pattern (like above) or enum like
"enum": ["persons"]
I couldn't get any clear answer from documentation, although in examples in specs enums are used for single values. So what's your opinion?
Ultimately, it doesn't really matter. Both will work and both are reasonable. That said, the most common approach I've seen is to use enum. Neither are perfect for readability, but I think enum is better for two reasons.
Using pattern requires two lines to express. Using enum requires only one because type is implied by the value in the array. Two lines are harder to read than one, so if that line is expressive enough, I say stick with one.
Not everyone is comfortable reading regex. enum might be more accessible for that reason.
Since draft-6 there is a new keyword called const for this use-case.
"type": {
"type": "string",
"const": "persons"
},
http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.1.3

jsonschema multiple values for string property

I have a json schema which describes a fairly complex API querying syntax. A few of the properties are pattern matched but also need to accept other values (i.e. other explicit strings) other than just the pattern. I can't seem to find anywhere in the multitude of json schema sites any examples of this.
An example:
{
"type": "object",
"properties": {
"$gte": {
"type": "string",
"pattern": "<some-pattern>"
}
}
}
What i'd like to be able to do in the example above is specify that $gte can be any of a certain set of constrained values. For example, this specific implementation requires that "$gte"'s values be constrained to one of the following:
A specific date format
A token {token} which gets replaced with a special value on the server-side
I've seen the oneOf property used in this situation but only with the format property so I'm assuming that this is possible, just not sure of the syntax of how to implement it, for instance it could be something like this:
{
"type": "object",
"properties": {
"$gte": {
"type": "string",
"oneOf": [
{"pattern": "<some-pattern>"},
"{token}",
"{another_token}"
]
}
}
}
Any clarity on how to accomplish this would be greatly appreciated as I'm not having much luck with the specification Draft 4 for json schema or in finding any examples.
If you want data to be one of a fixed set of exact values, you can use enum:
{
"type": "string",
"enum": ["stop", "go"]
}
So, to fit this in your example, try:
{
"type": "object",
"properties": {
"$gte": {
"type": "string",
"oneOf": [
{"pattern": "<some-pattern>"},
{"enum": ["TOKEN", "ANOTHER_TOKEN"]}
]
}
}
}
In addition to what already said, for the date you can use "date" within the "oneOf"
https://json-schema.org/understanding-json-schema/reference/string.html#dates-and-times