Using a $ref and other properties within a JSON Schema - jsonschema

In a JSON Schema is is valid to have a $ref and then other properties within the same schema, for example.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"title": "My schema",
"properties": {
"scripts": {
"$ref": "#/definitions/scriptsBase",
"description": "More docs.",
"minLength": 10
}
},
"definitions": {
"scriptsBase": {
"type": "string",
"description": "Base Description",
"minLength": 5
}
}
}
If this is allowable, then what are the rules when it comes to resolving properties defined in the $refed and the $refing schemas (in this example minLength and description. But potentially this could become much more complex if allOf etc where defined in both.

Found the answer in json schema property description and "$ref" usage, basically if a $ref exists all other properties are ignored.
https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03#section-3
Syntax
A JSON Reference is a JSON object, which contains a member named
"$ref", which has a JSON string value. Example:
{ "$ref": "http://example.com/example.json#/foo/bar" }
If a JSON value does not have these characteristics, then it SHOULD
NOT be interpreted as a JSON Reference.
The "$ref" string value contains a URI [RFC3986], which identifies
the location of the JSON value being referenced. It is an error
condition if the string value does not conform to URI syntax rules.
Any members other than "$ref" in a JSON Reference object SHALL be
ignored.

Related

Does Json Schema allow a property's definition to reference another property?

I'd like to create a JSON schema that restricts one property's values based on another property's values.
An example valid object might look like this:
{
"lookup": {
"foo": "string",
"bar": "number",
},
// properties in `values` must exist in `lookup`
"values": {
// `foo` must be a string
"foo": "string is OK",
// `bar` must be a number
"bar": 100
}
}
The idea is for the schema to enforce a relationship between the two properties.
{
"type": "object",
"properties": {
"lookup" : {
"type": "object",
"additionalProperties" : {
"type": "string",
"enum": ["string", "number"]
}
},
"values": {
"type": "object",
// - this value's properties must exist in `lookup`
// - if the property in `lookup` is set to `string`, the type here must be `string`; if the property in `lookup` is set to `number`, the type here must be `number`
}
}
}
This is possible in some cases. While you can't restrict a piece of data to certain values taken from other parts of the data (for example: using property X to provide a list of values that property Y can have), you can specify conditionals between parts of your schema.
requirement 1: this value's properties must exist in lookup -> not possible
requirement 2: if the property in lookup is set to string, the type here must be string; if the property in lookup is set to number, the type here must be number -> possible
See https://json-schema.org/understanding-json-schema/reference/conditionals.html for the various options available to you.
This is not possible with the standard specification of JSON Schema.

In JSON schema, define and reference a reusable enum type?

I noticed the following: Reusable enum types in json schema , which talks about defining a reusable enum type in JSON schema.
I would have assumed USING this reusable enum type would be trivial, simply specifying (in this case) the value of "MyEnum" for a "type" value.
I don't know if the results from Oxygen XML are authoritative, but I tried something like the following:
{
"$schema": "https://json-schema.org/draft/2019-09/schema#",
"type": "object",
"properties": {
"content": {"$ref": "#/definitions/content_type"}
},
"additionalProperties": false,
"definitions": {
"costCategory_type": {
"type": "object",
"enum": ["VH", "H", "M", "L"]
},
"allowedDevices_type": {
"type": "object",
"properties": {
"costCategory": {
"type": "costCategory_type"
},
On the line near the bottom of this, where I reference "costCategory_type", Oxygen gives me a syntax error, saying
#/definitions/allowedDevices_type/properties/costCategory/type: unknown type: [costCategory_type]
What am I missing?
Yes, the type keyword can only have values from the list null, boolean, object, array, string, number, integer. You can reference definitions with the $ref keyword:
...
"properties": {
"costCategory": {
"$ref": "#/definitions/costCategory_type",
}
}
(incidentally, your definition won't ever evaluate successfully as-is since you define it as being the "object" type, but the list of values in the enum are all strings.)

Valid name for definitions item

Is it correct to make a definition (suppose with name "abc") and then refer to it from an attribute called "abc" whose type is "array"? Or it's incorrect and array and its items have to have different names?
Thanks!
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "newSchema.json",
"title": "newSchema",
"type": "object",
"definitions": {
"abc": {
"properties": {
"some_col": {
"description": "hi",
"type": "integer"
}
}
}
},
"properties": {
"abc": {
"type": "array",
"items": {
"$ref": "#/definitions/abc"
}
}
}
}
It's a totally valid JSON structure and JSON Schema setup.
If you intend for others to read your generated schemas, you could add annotations to them to give additional information, such as "This is an array of [table]" and "this object represents a row in [table]".
See the Schema Annotations section of the JSON Schema draft-7 validation specification.

How do you extend json schema meta schema to support new properties?

I want to allow a $role properties anywhere in a json schema document where type is allowed. In theory I should be able to extend it as below where I do allOf against both the json schema meta-schema and my extension for $role which includes additionalProperties to pick up my extension meta-schema recursively. What I find is that I get validation for a top-level $role, but not for any embedded one. Am I missing something? Is there some other way I should extend the JSON Schema meta-schema?
I've tried a bunch of different validators and they all fail at validating the second $role in the example.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"role": {
"type": "string",
"pattern": "^((lg)|(unionType\\([a-zA-Z][a-zA-Z0-9.]*\\)))$"
},
},
"allOf": [
{
"$ref": "http://json-schema.org/draft-07/schema#"
},
{
"properties": {
"additionalProperties": {
"$ref": "#"
},
"$role": {
"oneOf": [
{
"$ref": "#/definitions/role"
},
{
"type": "array",
"items": {
"$ref": "#/definitions/role"
}
}
]
}
}
}
]
}
Example using this schema:
{
"$schema": "schema.json",
"title": "Prompt",
"$role": "unionType(Microsoft.IPrompt)",
"properties": {
"prompt": {
"type": "string",
"$role":"foo"
}
}
}
What I expect is that the second role should be invalid according to the schema. What I get is that it is valid even though it does not match the $role pattern. The first $role does validate successfully.
Yep, extending a meta schema is more complicated than it seems. Checkout the the JSON Hyper-Schema meta schema for an example of how to extend a meta schema. JSON Hyper-Schema adds two keywords: base and `links. When extending a schema, you need to redefine any recursive reference used in the original schema.
JSON Schemas (including meta-schemas) are immutable. You can't selectively modify an existing schema. Your meta schema only validates the $role keyword, all other keywords are validated by the draft-07 meta schema. Because your meta schema doesn't modify the draft-07 schema, keywords like properties are validated entirely within the context of the draft-07 schema and without knowledge of the new keyword you added in another schema.
It's unfortunate that so much duplication is involved in extending schemas and it is a problem that is being worked on. A potential solution to make this easier is slated to be introduced in the next draft.

reusing an object for multiple JSON schemas

I have two separate JSON schemas (used to validate HTTP request endpoints for a REST API) where they both accept the same exact object, but have different required fields (this is a create vs update request). Is there a way I can reuse a single definition of this object and only change the required fields? I know how to use $ref for reusing an object as a property of another object, but I cannot figure out how to reuse an entire object as the top-level object in a schema. My failed attempt so far:
event.json
{
"id": "event",
"type": "object",
"properties": {
"name": {
"type": "string"
},
"start_date": {
"type": "integer"
},
"end_date": {
"type": "integer"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}
event-create.json
{
"id": "event-create",
"type": "object",
"$ref": "event",
"additionalProperties": false,
"required": [ "name", "description" ]
}
Obviously that doesn't work. It seems like it tries to insert the entirety of 'event' into the definition of 'event-create', including the ID and such. I tried referincing event#/properties to no avail. I can't seem to do a $ref as the sole value inside a properties property either. Any ideas?
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
This is why your example doesn't work. Anything other than the $ref field is supposed to be ignored.
Support for $ref is limited to fields whose type is a JSON Schema. That is why trying to use it for properties doesn't work. properties is a plain object whose values are JSON Schemas.
The best way to do this is with allOf. In this case allOf can sort-of be thought of as a list of mixin schemas.
{
"id": "event-create",
"type": "object",
"allOf": [{ "$ref": "event" }],
"required": ["name", "description"]
}
I found some syntax that seems to work, but I'm not terribly happy with it:
{
"id": "event-create",
"allOf": [
{ "$ref": "event" },
{ "required": [ "name", "description" ] }
]
}
Seems like an abuse of the allOf operator, particularly for another case where there are no required fields (thus only one element insid the allof). But it works, so I'm going with it unless someone has a better idea.