JSON Schema reference resolution - json

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.

Related

Can json schema references have a template?

I'm working on reducing json schema, to try and make it easier to follow. Many of my nodes are of the same general form:
{
"[$PassedName]": {
"type": "object",
"properties": {
"current": {
"type": "object",
"[$PassedName]" : {"$ref": "#/definitions/Objects/[$PassedName]"}
},
"history": {
"type": "array",
"[$PassedName]" : {"$ref": "#/definitions/Objects/[$PassedName]"}
}
}
}
}
What I'd like to do is to pass a string to a reference, so the reference could use that string in the given locations. For example, giving the reference the string value "SSN" would have a resultant schema of the following:
{
"SSN": {
"type": "object",
"properties": {
"current": {
"type": "object",
"SSN": "#/definitions/Objects/SSN"
},
"history": {
"type": "array",
"SSN": "#/definitions/Objects/SSN"
}
}
}
}
Is this at all possible with json schemas? I've more than a dozen of basically the same node, so I'd like to simplify, if possible.

Is it possible to reference to JSON data itself within a schema definition?

Is it possible to define a JSON schema to check inner data integrity? So that mistakes like "ba" could be detected without further runtime checks.
Data JSON
{
"childNames": ["foo", "bar", "baz"],
"children": [
{
"name": "foo"
},
{
"name": "ba"
}
]
}
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://tbd.com/foo/bar",
"type": "object",
"properties": {
"childNames": {
"type": "array",
"items": { "type": "string" }
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": { "$ref": "???" }
}
}
}
}
}
}
If you're using .Net, then you can do this with JsonSchema.Net and JsonSchema.Net.Data.
You'll need to use a custom vocabulary which defines the data keyword.
Your schema will need to change to:
{
"$schema": "https://gregsdennis.github.io/json-everything/meta/data",
"$id": "https://tbd.com/foo/bar",
"type": "object",
"properties": {
"childNames": {
"type": "array",
"items": { "type": "string" }
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"data": {
"enum": "#/childNames"
}
}
}
}
}
}
}
Note the data keyword where your ??? was and the different meta-schema in the $schema keyword.
You can try it on https://json-everything.net/json-schema.
Information on how this works is here.
If you're not using .Net, you may need to figure out how to support this vocabulary (or work with the maintainer of the library you're using to do so).
Disclaimer: I'm the creator & maintainer of JsonSchema.Net and the other json-everything libraries.

JSON Schema Conditionals - Different Behaviour Between IDEs

I have two private IDE plugins that are generating JSON schemas to validate things. They are comprised of a bunch of definitions that are conditionally used with pattern regexs. The problem occurs in that the schema has different behaviours in IntelliJ and VS Code.
The schema is similar to below, however reduced and redacted:
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "array",
"additionalItems": true,
"definitions": {
"meat": {
"$id": "#/definitions/meat",
"type": "object",
"properties": {
"cut": {
"$id": "#/definitions/meat/cut",
"title": "cut",
"type": "string"
},
"fillet": {
"$id": "#/definitions/meat/fillet",
"title": "fillet"
},
"bones": {
"$id": "#/definitions/meat/bones",
"title": "bones",
"type": "string"
}
},
"required": [
"cut",
"fillet"
],
"additionalProperties": true
},
"fruit": {
"$id": "#/definitions/fruit",
"type": "object",
"properties": {
"pips": {
"$id": "#/definitions/fruit/pips",
"title": "pips",
"type": "string"
},
"stone": {
"$id": "#/definitions/fruit/stone",
"title": "stone"
},
"citrus": {
"$id": "#/definitions/fruit/citrus",
"title": "citrus",
"type": "string"
}
},
"required": [
"stone",
"pips"
],
"additionalProperties": true
}
},
"items": {
"$id": "#/items",
"type": "object",
"properties": {
"name": {
"$id": "#/items/name",
"type": "string",
"title": "Name"
},
"food": {
"$id": "#/items/food",
"type": "string"
}
},
"allOf": [
{
"if": {
"properties": {
"food": {
"pattern": "meat"
}
}
},
"then": {
"properties": {
"variants": {
"$ref": "#/definitions/meat"
}
}
}
},
{
"if": {
"properties": {
"food": {
"pattern": "fruit"
}
}
},
"then": {
"properties": {
"variants": {
"$ref": "#/definitions/fruit"
}
}
}
}
],
"additionalProperties": true
}
With YAML structure:
- name: orange
food: fruit
variants:
pips: true
So with food being fruit it will match, validate and suggest for that schema, and require stone and pips.
This works in VS Code. IntelliJ however, requires stone and pips correctly, however suggests keys from meat's properties.
This made me question if I have the correct implementation here - should I be using allOf, anyOf or oneOf here? I only need to match one. I also tried adding else: false, however that didn't change anything, and I still get suggestions for meat. When originally writing it, I think I found someone saying allOf will evaluate correctly as the unmatched patterns won't stop the merging of subschemas.
What I'm trying to identify is, is this an issue with my schema implementation, or a bug in IntelliJ's parsing of conditions? Or perhaps a bug in VS Code's parsing that has let me get away with faulty schemas.

JSON Schema if-else condition complex scenario

{
"policyHolder": {
"fullName": "A"
},
"traveller": [
{
"fullName": "B",
"relationship": "Spouse"
},
{
"fullName": "A",
"relationship": "My Self"
}
]
}
In above json, I want to validate that
if "relationship" = "My Self" then fullName must match the fullName in policyHolder
A field relationship must exist in traveller array, else json is invalid
I have tried to create a json schema with if-else, allOf, etc. but nothing works which can do these validations but not able to.
Please help!!
Schema:
{
"type": "object",
"required": [
"policyHolder",
"traveller",
],
"properties": {
"policyHolder": {
"$id": "#/properties/policyHolder",
"type": "object",
"required": [
"fullName"
],
"properties": {
"fullName": {
"$id": "#/properties/policyHolder/properties/fullName",
"type": "string",
}
}
},
"traveller": {
"$id": "#/properties/traveller",
"type": "array",
"minItems": 1,
"items": {
"$id": "#/properties/traveller/items",
"type": "object",
"properties": {
"fullName": {
"$ref": "#/properties/policyHolder/properties/fullName"
},
"relationship": {
"$id": "#/properties/traveller/items/properties/relationship",
"type": "string",
}
},
"required": [
"fullName",
"relationship"
],
}
}
}
}```
It's your first requirement that you're going to have the most trouble with. JSON Schema doesn't support validation of data against data elsewhere in the instance. It's a highly discussed topic, but nothing has been adopted yet. I suggest you verify this with a little code.
For the second, I would suggest you extract some of your subschemas into definitions rather than trying to muck about with IDs. IDs are typically more beneficial if you're referencing them from other documents or if you use short (like single-word) IDs. Defining the ID as its location in the document is redundant; most processors will handle this automatically.
{
"type": "object",
"required": [
"policyHolder",
"traveller",
],
"definitions": {
"person": {
"type": "object"
"properties": {
"fullName": {"type": "string"}
},
"required": ["fullName"]
},
"relationship": { "enum": [ ... ] } // list possible relationships
},
"properties": {
"policyHolder": { "$ref": "#/definitions/person" },
"traveller": {
"type": "array",
"minItems": 1,
"items": {
"allOf": [
{ "$ref": "#/definitions/person" },
{
"properties": {
"relationship": { "$ref": "#/definitions/relationship" }
},
"required": ["relationship"]
}
]
}
}
}
}
(I extracted the relationship into its own enum definition, but this is really optional. You can leave it inline, or even an unrestricted string if you don't have a defined set of relationships.)
This can't currently be done with JSON Schema. All JSON Schema keywords can only operate on one value at a time. There's a proposal for adding a $data keyword that would enable doing this kind of validation, but I don't think it's likely to be adopted. $data would work like $ref except it references the JSON being validated rather than referencing the schema.
Here's what how you would solve your problem with $data.
{
"type": "object",
"properties": {
"policyHolder": {
"type": "object",
"properties": {
"fullName": { "type": "string" }
}
},
"traveler": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fullName": { "type": "string" },
"relationship": { "type": "string" }
},
"if": {
"properties": {
"relationship": { "const": "My Self" }
}
},
"then": {
"properties": {
"fullName": { "const": { "$data": "#/policyHolder/fullName" } }
}
}
}
}
}
}
Without $data, you will have to do this validation in code or change your data structure so that it isn't necessary.

Required fields for properties within an array of objects schema definition

I have following schema definition. library is required. there can be many. so in that library object there are some other properties which is required. But though i use following code it is not validating. I mean book title is not required. title of the author is required.How can i fix this?
schema definition
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": ["library"],
"properties": {
"library": {
"type": "array",
"items": [
{"required":["title"],
"type": "object",
"properties": {
"title": {
"type": "string"
},
"author": {
"type": "string"
}
}
}
]
}}}
json input
{"library":[]
}
expected json input
{"library":[{"title":"Max"}]
}
I assume you want "json input" to fail.
If so, your problem is that you didn't describe a library with a book with missing title, Instead, your document says 'I am a library with no books'.
Failing JSON:
{"library":[{}]}
Alternately, if you want to probibit empty libraries: JSON Schema has a minLength property.
Your question is a little difficult to understand, but I noticed that you are using the tuple form of the items keyword. I'm sure that is not what you want. Try the following schema and I think it will behave the way you expect.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": ["library"],
"properties": {
"library": {
"type": "array",
"items": {
"required": ["title"],
"type": "object",
"properties": {
"title": {
"type": "string"
},
"author": {
"type": "string"
}
}
}
}
}
}