I am trying to model documents (such as passport) and their attributes (first name, birth date, etc) and describe them using json schema.
I want to define many types of documents in runtime, every document as a separate schema.
Some attributes may be referenced from multiple documents, such as firstName in passport and in driver's license, so it is
probably wise to create a separate attribute definition schema for this.
Lets say, I have a passport, its model looks like following:
{
"owner": "<string>",
"type" : "<string>",
"salt" : <number>,
"attributes": {
"number" : {"value": "<string>", "salt": <number>}
"lastName" : {"value": "<string>", "salt": <number>},
"firstName" : {"value": "<string>", "salt": <number>},
"birthDate" : {"value": "<date>", "salt": <number>},
"nationality": {"value": "<string>", "salt": <number>},
}
}
Every attribute has "value" and "salt", but "value" is different for every key (attribute name).
My question is, how will schema for this type of JSON document look?
UPD: every attribute has value and salt. Salt is exactly equal for all attributes, but value is different. If I manually specify that every attribute has salt and value, then I will end up with huge schema duplication. Is it possible to avoid it?
There are plenty of things you can do to reduce the duplication in your schema. One way is to use the definitions keyword. Another option is to use patternProperties to validate common constraints across multiple properties.
{
"type": "object",
"properties": {
"owner": { "type": "string" },
"type": { "type": "string" },
"salt": { "$ref": "#/definitions/salt" },
"attributes": {
"type": "object",
"patternProperties": {
".*": {
"type": "object",
"properties": {
"salt": { "$ref": "#/definitions/salt" }
},
"required": ["value", "salt"]
}
},
"properties": {
"number": { "$ref": "#/definitions/string-value" },
"lastName": { "$ref": "#/definitions/string-value" },
"firstName": { "$ref": "#/definitions/string-value" },
"birthDate": { "$ref": "#/definitions/date-value" },
"nationality": { "$ref": "#/definitions/string-value" }
}
}
},
"definitions": {
"salt": { "const": 1234 },
"string-value": {
"properties": {
"value": { "type": "string" }
}
},
"date-value": {
"properties": {
"value": { "type": "string", "format": "date" }
}
}
}
}
With draft-7 of JSON Schema (current at time of writing), assuming you'll want to be using references to another file which contains your property definitions, you may want to do something like the following.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"number": {
"properties": {
"value": {...}, # Whatever you define as your schema for number/value.
"salt": {
"$ref": "externalRef/definitions/salt"
}
}
}
}
}
Related
I have a JSON schema that contains "$ref" tags and I am trying to get a version of the JSON schema that have the "$ref" tags resolved. I am only looking to resolve "$ref" from definition (tags) within the JSON Schema string (ie. not external resolution needed).
Is there a library that performs the resolution of the JSON Schema? (I am currently using org.everit.json.schema library, which is great, but I can't find how to do what I need).
For example, my original schema is:
{
"$id": "https://example.com/arrays.schema.json",
"description": "A representation of a person, company, organization, or place",
"title": "complex-schema",
"type": "object",
"properties": {
"fruits": {
"type": "array",
"items": {
"type": "string"
}
},
"vegetables": {
"type": "array",
"items": { "$ref": "#/$defs/veggie" }
}
},
"$defs": {
"veggie": {
"type": "object",
"required": [ "veggieName", "veggieLike" ],
"properties": {
"veggieName": {
"type": "string",
"description": "The name of the vegetable."
},
"veggieLike": {
"type": "boolean",
"description": "Do I like this vegetable?"
}
}
}
}
}
Which would resolve to something like this (notice that the "#defs/veggie" resolves to its definition inserted inline in the schema):
{
"$id": "https://example.com/arrays.schema.json",
"description": "A representation of a person, company, organization, or place",
"title": "complex-schema",
"type": "object",
"properties": {
"fruits": {
"type": "array",
"items": {
"type": "string"
}
},
"vegetables": {
"type": "array",
"items": {
"type": "object",
"required": [ "veggieName", "veggieLike" ],
"properties": {
"veggieName": {
"type": "string",
"description": "The name of the vegetable."
},
"veggieLike": {
"type": "boolean",
"description": "Do I like this vegetable?"
}
}
}
}
}
}
This isn't possible in the general sense, because:
the $ref might be recursive (i.e. reference itself again)
the keywords in the $ref might duplicate some of the keywords in the containing schema, which would cause some logic to be overwritten.
Why do you need to alter the schema in this way? Generally, a JSON Schema implementation will resolve the $refs automatically while evaluating the schema against provided data.
I am working on updating a JSON schema for work.
For the json array, we have
"accountsInfo": [{
"type":"ADMIN",
"firstName":"Bill",
"lastName":"Cipher",
"emailAddress":"bcipher#gfalls.com"
}, {
"type":"USER"
"firstName":"Bugs",
"lastName":"Bunny",
"emailAddress":"whats#updoc.org"
}]
The USER type is needs to be optional for this schema, with the atleast 1 ADMIN type is required in the array. How can I do this?
Here is the portion of the schema file. It is using Json Schema 7.
"accountsInfo": {
"type": "array",
"uniqueItems": true,
"minItems": 2,
"items": [
{
"type": "object",
"required": [
"type",
"firstName",
"lastName",
"emailAddress"
],
"properties": {
"type": {
"type": "string",
"enum": [
"ADMIN",
"USER"
]
},
"firstName": {
"type": "string",
"$ref": "#/definitions/non-empty-string"
},
"lastName": {
"type": "string",
"$ref": "#/definitions/non-empty-string"
},
"emailAddress": {
"type": "string",
"format": "email"
}
}
}
]
}
You can use the "contains" keyword for this. In pseudocode: "the array must contain (at least one) item that successfully evaluates against this schema".
As a sibling keyword to "type": "object" and "items": { ... }, add:
"contains": {
"properties": {
"type": {
"const": "ADMIN"
}
}
}
Also, you have an error in your "items" keyword: if you intend for that subschema to match all items, not just the first, remove the extra array around the schema. The array form of "items" matches each item in the data against each item in the schema in turn, and you only specify a schema for the first item, so all items after the first can be anything.
"items": { .. schema .. } not "items": [ { .. schema .. } ].
If using the contains keyword as suggested, and if you are using strict mode, you may need to add "type": "array" like this:
{
"type": "array",
"contains": {
"properties": {
"type": {
"const": "ADMIN"
}
}
}
}
I'm trying to create a Json schema for an object. Is it possible to reference the top level root object, when defining an embedded object within it?
I've seen that I can reuse a definition through $ref:"#/definitions/..." but I am trying to reuse the whole top level object. Not just the properties within.
if my json looks like this:
{
"name": "brodie",
"age": 2,
"bestFriend": {
"name": "clara",
"age": 4,
"bestFriend": {
"name": "stella",
"age" : 5
}
}
}
I can create a json schema that looks like this:
{
"type": "object",
"properties": {
"name": {
"$ref": "#/definitions/name"
},
"age": {
"$ref": "#/definitions/age"
},
"bestFriend": {
"$ref": "#/definitions/bestFriend"
},
},
"definitions": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"bestFriend": {
"type": "object",
"allOf": [
{"$ref": "#/defintions/name"},
{"$ref": "#/definitions/age"},
{"$ref": "#/definitions/bestFriend"}
]
}
}
}
But I'd like to do something like
{
"type": "object",
"title": "bestFriend",
"properties": {
"$ref": "#/definitions/bestFriend"
},
"definitions": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"bestFriend": {
"type": "object",
"allOf": [
{"$ref": "#/defintions/name"},
{"$ref": "#/definitions/age"},
{"$ref": "#/definitions/bestFriend"}
]
}
}
}
I am using the same json object for all 3 instances brodie, clara, stella. I want to encapsulate the top level object as one definition so I can reference it later in a cleaner way. It would be nice if I didn't have the list of $refs at the top and only had them within the "definitions" object at the bottom. Is this even possible? Or is what I have above the recommended pattern.
(I know I could define some of these fields inline, but I plan on heavily reusing some json objects, and felt it would be cleaner if everything was defined in the "definitions" section.)
Given a JSON schema with the following in the definitions section:
"phoneNumber": {
"type": "object",
"properties": {
"countryCode": {
"type": "number"
},
"areaCode": {
"type": "number"
},
"number": {
"type": "number"
},
"extension": {
"type": "number"
},
"service": {
"type": "string",
"enum": ["Voice", "Fax", "Data"]
},
"class": {
"type": "string",
"enum": ["Switchboard", "Direct", "PA", "Mobile"]
}
}
}
If I want to include phoneNumber elsewhere using a $ref and want the JSON to validate if it contains multiple occurrences of phoneNumber, can I use maxItems/minItems:
"person": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"phoneNumber": {
"$ref": "#/definitions/phoneNumber"
//can I use maxItems/minItems here?
}
}
}
Can I use maxItems and minItems here, or would I have to do something like this below for it to validate:
"phoneNumber": {
"allOf": { "$ref": "#/definitions/phoneNumber" },
"maxItems": 4
}
$ref must stand alone. The option you identified using allOf is the best way to do it.
Any members other than "$ref" in a JSON Reference object SHALL be ignored.
https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03#section-3
I'm completely new to json and json schema, so I have a question (yet I don't know how much it make sense). Can we create a json schema which is common for similar type of structure. For example:
One single schema can be used to validate following json
JSON:
{
"Team_Table":
[{"Name":"New Zealand", "Match":"Six", "Won":"Six"}]
}
And
{
"Story_Taller":
[{"Story":"No Name", "Chapter":"Don't know"}]
}
Similarities:
Both have only one object in the array
Objects have string value.
Dissimilarities:
Number of properties are different
Keys are different in both
Can we do this?
Maybe this helps you along:
{
"properties": {
"Story_Taller": {
"type": "array",
"maxItems": 1,
"items": {
"properties": {
"Chapter": {
"type": "string"
},
"Story": {
"type": "string"
}
},
"additionalProperties": false
}
},
"Team_Table": {
"type": "array",
"maxItems": 1,
"items": {
"properties": {
"Name": {
"type": "string"
},
"Match": {
"type": "string"
},
"Won": {
"type": "string"
}
},
"additionalProperties": false
}
}
},
"oneOf": [
{
"title": "Story_Taller",
"required": [
"Story_Taller"
]
},
{
"title": "Team_Table",
"required": [
"Team_Table"
]
}
]
}
in (short) words:
in your JSON there must be one property of either "Story_Taller" or "Team_Table" with a maximum of 1 item
"oneOf": [ ... ]
Properties of both arrays are defined by items
"Story_Taller" must have "Chapter" and "Story" and no additional properties.
"Team_Table" must have "Name", "Match", "Won" and no additional properties.
And all of them are defined as strings.