I have this schema. It checks comments, and works fine at the moment.
var schema = {
id: '',
type: 'object',
additionalProperties: false,
properties: {
text: {
type: 'string',
minLength: 1,
required: true
},
author: {
type: 'number',
required: true
}
}
};
My comment structure is:
{
text: "Hello world!",
author: 1
}
But now, I need to validate an array of objects like this. So I can get something like:
[
{
text: "Hello world! Im comment #1",
author: 1
},
{
text: "Super awesome comment #2!",
author: 0
}
]
Sometimes I get one comment only so I get one object, and need to use first schema, but sometimes I get an array of comments, and my schema does not fit.
I heard about json schema anyOf, but I dont know how to do it.
Some like:
anyOf
schema-1 (object)
schema-2 (array with objects)
Any help?
Thanks.
The solution is to have a common definition in one place, and then reference that common definition from two different options inside oneOf:
Here, we put the simple object definition inside definitions:
{
"definitions": {
"singleObject": {
... same definition as in your question ...
}
}
}
We then reference this schema, inside oneOf:
{
"oneOf": [
{"$ref": "#/definitions/singleObject"}, // plain object
{
"type": "array", // array of plain objects
"items": {"$ref": "#/definitions/singleObject"}
}
],
"definitions": {
"singleObject": {...}
}
}
You can organise this a few different ways - I personally often end up with the simple-object definition as the root schema, and have the single/array switcher in definitions, so the schema for my documents is actually http://example.com/schema#/definitions/arrayOrSingle.
Related
I have a JSON schema I am trying to describe, a JSON object which has a additionalProperties node which contains an array of key value pairs.
{
"additionalProperties": [
{
"key": "optionA",
"value": "1"
},
{
"key": "optionB",
"value": "0"
},
{
"key": "optionC",
"value": "1"
}
],
}
Whilst I can use quite a generic schema for this like this
additionalProperties:
properties:
key:
type: string
value:
type: string
required:
- key
- value
type: object
I ideally wish to explain what the various keys that can appear and what they mean. I.e. optionA means this and OptionB means that. Is there a way I can describe the exact options which will appear in the array?
The description field is used when you want to provide additional information or context to the reader that isn't necessarily explained by schema alone.
additionalProperties:
description: Your explanation goes here. Note that you can use markdown formatting if desired.
properties:
key:
type: string
value:
type: string
required:
- key
- value
type: object
You can also more accurately describe your options in the schema if they are all known values using oneOf, allOf, or anyOf. (Documentation here)
additionalProperties:
properties:
anyOf:
- $ref: '#/components/schemas/optionA'
- $ref: '#/components/schemas/optionB'
- $ref: '#/components/schemas/optionC'
I'm trying to write a schema to validate a yaml file after parsing it into JSON.
Supposing that this is my .yml file with 2 top level properties, cars and garage.
cars is optional while garage is required.
However, one of garage's sub-properties is cars. If cars under garage is defined, I want the schema to make sure that cars at the top level is also defined. Otherwise, the schema is not going to be valid
cars:
- BMW
- Mercedes-Benz
- Audi
garage:
location: Miami
cars:
- BMW
- Audi
My Schema:
{
properties: {
cars: {
type: 'array',
items: {
type: 'string'
}
},
garage: {
type: 'object',
properties: {
location: {
type: 'string'
},
cars: {
type: 'array'
}
},
required: ['garage']
}}
So I tried doing an if-else at the top level
{
if: { properties: { garage: { cars: {type: 'array'}}}},
then: {required:['cars']},
properties: {
cars: {
type: 'array',
items: {
type: 'string'
}
},
garage: {
type: 'object',
properties: {
location: {
type: 'string'
},
cars: {
type: 'array'
}
},
required: ['garage']
}}
But it seems that I'm doing it wrong or it doesn't serve that purpose.
Also doing anyOf at the top level to match sub-schemas didn't work for me..
Any help ?
You can specify the referential integrity constraint (together with all the other requirements) using the "JSON Extended Structure Schema" language, JESS.
Here is the complete JESS schema presented as a single JSON document:
[ "&",
["&",
{"::>=": {"garage": {"location": "string", "cars": [ "string" ] } } }
],
{"ifcond": { "has": "cars" },
"then": ["&", { "forall": ".[cars]", "schema": ["string"] } ]
},
{"setof": ".[garage]|.[cars][]", "subsetof": ".[cars][]"}
]
The first "&" introduces the conjunction of the three requirements, the last of which is the referential integrity constraint.
The JESS repository has a schema conformance checker, which I used to verify your sample (expressed as JSON) against the above schema.
The value of if must be a JSON Schema.
If you take the value of if as a JSON Schema on it's own and test the validation result of applying it to the correct location in your JSON instance, it may help you debug this type of issue.
In your if block, you need to put nest cars under properties, just like you've done in your main schema.
You may also want to make both garage and cars required in your if block.]
You cannot however define that you want the values from garage.cars to be included in your cars array.
I have a collection in my MongoDB with 13 million of records. Unfortanelly, when I created this collection, no schema was created for it. I would like to know if there is any method that I could add an JSON schema beyond backup the entire the database, create the schema and upload all the data.
You can apply a JSON schema to an existing collection using the collMod command to add a new JSON schema to the collection https://docs.mongodb.com/manual/core/schema-validation/. An example below. However it will only apply to new write operations it will not run against existing documents in the collection.
db.runCommand( {
collMod: "contacts",
validator: { $jsonSchema: {
bsonType: "object",
required: [ "phone", "name" ],
properties: {
phone: {
bsonType: "string",
description: "must be a string and is required"
},
name: {
bsonType: "string",
description: "must be a string and is required"
}
}
} },
validationLevel: "moderate"
} )
Background
I'm building an API with Fastify and it's my first time using JSON schema validation. The idea is that it will both make the server code more efficient and help our developers as they learn how to consume my API.
Problem
I'm trying to validate a route that allows the client to query kittens by name only. A successful formed query would look /kittens?name=fluffykins.
My schema for this route looks like this:
{
querystring: {
type: 'object',
name: { type: 'string' },
}
}
Question
How can I make my schema validator accept only queries on name and reject other queries like /kittens?age=1? My preference is for the schema validator to handle it independently of my controller code and for it to also support queries that we may add in the future.
Thanks!
As is typical of when I post a question to SO, I find an answer myself shortly after. The following is what worked for me but I'm still interested to hear if there are other better ways of doing this!
{
querystring: {
type: 'object',
properties: {
name: { type: 'string' }
},
anyOf: [
{
required: [ 'name' ]
}
],
},
}
I'm not quite sure what you are trying to do with the anyOf, so I might be missing something, but I believe this is what you want (if you are using draft-06 or later):
{
"type": "object",
"required": ["name"],
"propertyNames": {"enum": ["name"]},
"properties": {
"name": {"type": "string"}
}
}
The propertyNames ensures that name is the only acceptable property. You can also do this by setting "additoinalProperties": false instead (if you are useing draft-04 you have to do this as it does not support propertyNames). But doing that can cause unexpected problems when you try to combine schemas, so if you can use draft-06 propertyNames is more flexible.
Here's the draft-04 version:
{
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"}
},
"additionalProperties": false
}
I am using nodejs with jsonpath.
I have this json structure:
{
things:{
books: [
{name: "book1"},
{name: "book2"},
{name: "book3"},
{name: "book4"},
],
movies: [
{name: "movie1"},
{name: "movie2"},
{name: "movie3"},
{name: "movie4"},
]
}
}
I would like to know the jsonpath expression that returns an array with the key names of the things object. That would be:
["books","movies"]
For now, I am doing this:
Object.keys(jsonpath.eval(jsonStructure,"$.things").pop());
But I don't find it elegant... I should not need to get a copy the whole structure when I only need the key names.
jsonPath has new update jsonpath-plus
jsonpath-plus expands on the original specification to add some additional operators and makes explicit some behaviors the original did not spell out.
^ for grabbing the parent of a matching item
~ for grabbing property names of matching items (as array)
so to get proper output use this query things.*~
you can try here also https://jsonpath.com/
I don't believe there is a better solution than your own:
Object.keys(jsonpath.eval(jsonStructure,"$.things").pop());
I think the main misconception here is that you don't have to worry about this snippet "getting a copy of the whole structure", because you aren't copying the whole structure. You already have the entire object loaded into memory, jsonpath doesn't create a new copy, it simply returns a reference to the already existing object, i.e.:
jsonpath.eval(jsonStructure,"$.things").pop() === jsonStructure.things //true
Not exactly what you are asking for, but might still be relevant.
We use object-scan for this kind of task as it is much better suited for data processing and analyzing. Once you wrap your head around it that is (:
Anyways, here is how you could answer your question if you are willing to add another dependency
// const objectScan = require('object-scan');
const data = { things: { books: [ { name: 'book1' }, { name: 'book2' }, { name: 'book3' }, { name: 'book4' } ], movies: [ { name: 'movie1' }, { name: 'movie2' }, { name: 'movie3' }, { name: 'movie4' } ] } };
console.log(objectScan(['things.*'], { rtn: 'property' })(data));
// => [ 'movies', 'books' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
The syntax you used for give is wrong
to get keys in json path use "$.*~"
ex.
input:
{
"firstName": "John",
"lastName" : "doe",
"age" : 26
}
output:
[
"firstName",
"lastName",
"age"
]