Reputation: 23
I have a moderately complex JSON schema containing an array which shall only accept items of two specific types. For some reason, after adding an item of a third type, validation succeeds. Can you help me find what I overlook?
I've made a small fragment of the scheme with only the affected parts:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"trainzMeshes": {
"title": "Trainz Meshes",
"description": "Specifications of Trainz meshes to create as FBX files and comments specified as an array.",
"type": "array",
"additionalItems": false,
"items": {
"anyOf": [
{
"comment": {
"$ref": "#/$defs/comment"
},
"minItems": 0
},
{
"$ref": "#/$defs/trainzMesh",
"minItems": 1
}
]
}
},
},
"$defs": {
"comment": {
"title": "Comment",
"description": "A comment to help you remember what comes next. You may include as many comments as you wish.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"trainzMesh": {
"title": "Trainz Mesh Composition Wrapper",
"description": "Container for the specification of a Trainz mesh describing what FBX file and Trainz asset to create from which of your Blender objects.",
"type": "object",
"required": ["trainzMeshComposition"],
"additionalProperties": false,
"properties": {
"trainzMeshComposition": {
"title": "Trainz Mesh Composition Settings",
"description": "Specification of a Trainz mesh describing what FBX file and Trainz asset to create from which of your Blender objects. You may include comments as well.",
"type": "object",
"required": ["name", "blenderExportSpecifications"],
"additionalProperties": false,
"properties": {
"comment": {
"$ref": "#/$defs/comment"
},
"name": {
"title": "Trainz Mesh File Name (Pattern)",
"description": "Name (pattern) for the FBX files to create.",
"type": "string"
},
"blenderExportSpecifications": {
"title": "Blender Export Specifications",
"description": "Specifies one or more Blender objects, with optionally assigning a material and UV map to each, to include them in the Trainz mesh FBX file.",
"type": "array",
"minItems": 1,
"additionalItems": false,
"items": {
"anyOf": [
{ "$ref": "#/$defs/blenderExportSpecification",
"minContains": 1
},
{
"comment": {
"$ref": "#/$defs/comment"
},
"minContains": 0
}
]
}
}
}
}
}
},
"blenderExportSpecification": {
"title": "Blender Export Specification",
"description": "Specifies one or more Blender objects, with optionally assigning a material and UV map to each, to include them in the Trainz mesh FBX file.",
"type": "object",
"anyOf": [
{ "required": ["scopeToCollections"] },
{ "required": ["includeObjectsByName"] },
{ "required": ["includeObjectsByPattern"] },
{ "required": ["includeObjectsByConditions"] },
{ "required": ["excludeObjectsByName"] },
{ "required": ["excludeObjectsByPattern"] },
{ "required": ["excludeObjectsByConditions"] },
{ "required": ["dropModifiers"] },
{ "required": ["material"] },
{ "required": ["uvMap"] }
],
"additionalProperties": false,
"properties": {
"scopeToCollections": {
"title": "Scope to Collections",
"description": "A regular expression specifying collection name patterns or a list specifying collection names. Only Blender objects contained under referenced collections in your Blender file will be processed for inclusion for exporting.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"includeObjectsByName": {
"title": "Include Object(s) By Name",
"description": "The name or (in the form of a string array) names of the Blender object(s) to export and to which parameters in this export specification apply.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"includeObjectsByPattern": {
"title": "Include Objects By Name Pattern",
"description": "A regular expression specifying an object name pattern. Only objects within the scope selected by scopeToCollections and matching this pattern will be exported.",
"type": "string",
"format": "regex"
},
"includeObjectsByConditions": {
"title": "Include Objects By Condition",
"description": "A pair of a property name and a value pattern. Only Blender objects having this property with a value matching the specified value pattern will be included in the export.",
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/objectPropertyCondition"
}
},
"excludeObjectsByName": {
"title": "Exclude Object(s) By Name",
"description": "Name(s) of object(s) to omit when exporting within this export specification.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"excludeObjectsByPattern": {
"title": "Exclude Objects By Name Pattern",
"description": "A regular expression specifying an object name pattern. Matching objects will be omitted when exporting within this export specification.",
"type": "string",
"format": "regex"
},
"excludeObjectsByConditions": {
"title": "Exclude Objects By Condition",
"description": "A pair of a property name and a value pattern. Blender objects having this property with a value matching the specified value pattern will not be included in the export.",
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/objectPropertyCondition"
}
},
"dropModifiers": {
"title": "Modifiers to Drop",
"description": "Name(s) of modifiers to hide before exporting the objects.",
"oneOf": [
{
"title": "Name Pattern for Modifiers to Drop",
"description": "A regex. Modifiers with a name matching this regex will not be applied.",
"type": "string",
"format": "regex"
},
{
"title": "Names of Modifiers to Drop",
"description": "A list of names of modifiers to hide before exporting.",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"material": {
"title": "Material to Apply",
"description": "Name of the material in your Blender file to apply to the object before exporting. The name shall conform to Trainz specifications.",
"type": "string"
},
"uvMap": {
"title": "UV Map to Apply",
"description": "Name of the UV map created for the Blender object to switch to before exporting.",
"type": "string"
}
}
},
"objectPropertyCondition": {
"title": "Object Property Condition",
"description": "Description of a condition composed of a custom property defined for Blender objects and a pattern for matching values",
"required": ["name", "valuePattern"],
"additionalProperties": false,
"properties": {
"name": {
"title": "Condition Property Name",
"description": "Name of the property containing the condition.",
"type": "string"
},
"valuePattern": {
"title": "Condition Value Pattern",
"description": "A pattern which the value of the property shall match to meet the condition.",
"type": "string",
"format": "regex"
}
}
}
}
}
And I have this JSON based on that scheme:
{
"trainzMeshes": [
{ "comment": "*****************************************************************" },
{ "comment": " STATION NAME DISPLAY - SIZE S " },
{ "comment": "*****************************************************************" },
{ "comment": "---------------------------- Lod 0 ------------------------------" },
{
"trainzMeshComposition": {
"name": "name-display-S-daytime-lod0n",
"blenderExportSpecifications": [
{
"includeObject": [
"Name Display Console - S [lod0]",
"Name Display - S - Default [lod0]"
]
}
]
}
}
]
}
The validator I have for VS Code, as well as Newtonsoft's validator at https://www.jsonschemavalidator.net/ finds that the JSON conforms to the schema, whereas I would expect "includeObject" to violate it for two reasons.
I tried playing with a lot of things, such as changing "oneOf" to "anyOf", to change the oneOf > items structure to items > oneOf, but no way could I make it fail.
Upvotes: 0
Views: 194
Reputation: 3307
Your anyOf
schemas are unconstrained because you defined comment
as a property, but you didn't use the properties
keyword, which JSON Schema interprets as the property comment
is unconstrained. Hopefully that makes sense.
You also have some invalid usage of minItems
, and minContains
which goes unused because you are not using contains
keyword. Also, the location of these keywords used would be incorrect as a sibling to the $ref
.
try this...
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"trainzMeshes": {
"title": "Trainz Meshes",
"description": "Specifications of Trainz meshes to create as FBX files and comments specified as an array.",
"type": "array",
"additionalItems": false,
"items": {
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"comment": {
"$ref": "#/$defs/comment"
}
}
},
{
"$ref": "#/$defs/trainzMesh"
}
]
}
}
},
"$defs": {
"comment": {
"title": "Comment",
"description": "A comment to help you remember what comes next. You may include as many comments as you wish.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"trainzMesh": {
"title": "Trainz Mesh Composition Wrapper",
"description": "Container for the specification of a Trainz mesh describing what FBX file and Trainz asset to create from which of your Blender objects.",
"type": "object",
"required": [
"trainzMeshComposition"
],
"additionalProperties": false,
"properties": {
"trainzMeshComposition": {
"title": "Trainz Mesh Composition Settings",
"description": "Specification of a Trainz mesh describing what FBX file and Trainz asset to create from which of your Blender objects. You may include comments as well.",
"type": "object",
"required": [
"name",
"blenderExportSpecifications"
],
"additionalProperties": false,
"properties": {
"comment": {
"$ref": "#/$defs/comment"
},
"name": {
"title": "Trainz Mesh File Name (Pattern)",
"description": "Name (pattern) for the FBX files to create.",
"type": "string"
},
"blenderExportSpecifications": {
"title": "Blender Export Specifications",
"description": "Specifies one or more Blender objects, with optionally assigning a material and UV map to each, to include them in the Trainz mesh FBX file.",
"type": "array",
"minItems": 1,
"additionalItems": false,
"items": {
"anyOf": [
{
"$ref": "#/$defs/blenderExportSpecification"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"comment": {
"$ref": "#/$defs/comment"
}
}
}
]
}
}
}
}
}
},
"blenderExportSpecification": {
"title": "Blender Export Specification",
"description": "Specifies one or more Blender objects, with optionally assigning a material and UV map to each, to include them in the Trainz mesh FBX file.",
"type": "object",
"anyOf": [
{
"required": [
"scopeToCollections"
]
},
{
"required": [
"includeObjectsByName"
]
},
{
"required": [
"includeObjectsByPattern"
]
},
{
"required": [
"includeObjectsByConditions"
]
},
{
"required": [
"excludeObjectsByName"
]
},
{
"required": [
"excludeObjectsByPattern"
]
},
{
"required": [
"excludeObjectsByConditions"
]
},
{
"required": [
"dropModifiers"
]
},
{
"required": [
"material"
]
},
{
"required": [
"uvMap"
]
}
],
"additionalProperties": false,
"properties": {
"scopeToCollections": {
"title": "Scope to Collections",
"description": "A regular expression specifying collection name patterns or a list specifying collection names. Only Blender objects contained under referenced collections in your Blender file will be processed for inclusion for exporting.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"includeObjectsByName": {
"title": "Include Object(s) By Name",
"description": "The name or (in the form of a string array) names of the Blender object(s) to export and to which parameters in this export specification apply.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"includeObjectsByPattern": {
"title": "Include Objects By Name Pattern",
"description": "A regular expression specifying an object name pattern. Only objects within the scope selected by scopeToCollections and matching this pattern will be exported.",
"type": "string",
"format": "regex"
},
"includeObjectsByConditions": {
"title": "Include Objects By Condition",
"description": "A pair of a property name and a value pattern. Only Blender objects having this property with a value matching the specified value pattern will be included in the export.",
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/objectPropertyCondition"
}
},
"excludeObjectsByName": {
"title": "Exclude Object(s) By Name",
"description": "Name(s) of object(s) to omit when exporting within this export specification.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"excludeObjectsByPattern": {
"title": "Exclude Objects By Name Pattern",
"description": "A regular expression specifying an object name pattern. Matching objects will be omitted when exporting within this export specification.",
"type": "string",
"format": "regex"
},
"excludeObjectsByConditions": {
"title": "Exclude Objects By Condition",
"description": "A pair of a property name and a value pattern. Blender objects having this property with a value matching the specified value pattern will not be included in the export.",
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/objectPropertyCondition"
}
},
"dropModifiers": {
"title": "Modifiers to Drop",
"description": "Name(s) of modifiers to hide before exporting the objects.",
"oneOf": [
{
"title": "Name Pattern for Modifiers to Drop",
"description": "A regex. Modifiers with a name matching this regex will not be applied.",
"type": "string",
"format": "regex"
},
{
"title": "Names of Modifiers to Drop",
"description": "A list of names of modifiers to hide before exporting.",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
]
},
"material": {
"title": "Material to Apply",
"description": "Name of the material in your Blender file to apply to the object before exporting. The name shall conform to Trainz specifications.",
"type": "string"
},
"uvMap": {
"title": "UV Map to Apply",
"description": "Name of the UV map created for the Blender object to switch to before exporting.",
"type": "string"
}
}
},
"objectPropertyCondition": {
"title": "Object Property Condition",
"description": "Description of a condition composed of a custom property defined for Blender objects and a pattern for matching values",
"required": [
"name",
"valuePattern"
],
"additionalProperties": false,
"properties": {
"name": {
"title": "Condition Property Name",
"description": "Name of the property containing the condition.",
"type": "string"
},
"valuePattern": {
"title": "Condition Value Pattern",
"description": "A pattern which the value of the property shall match to meet the condition.",
"type": "string",
"format": "regex"
}
}
}
}
}
The diff
EDIT, based on your comment for additional requirements:
trainzMesh
object and any number of comment
objectsThis is one way to do it.
trainzMesh
object. We do that with items
as an array []
. This creates positional arguments.minItems: 1
to always require at least the trainzMesh
object. comment
s are optional.comment
object and no additionalProperties
are allowed in that object schema."$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"properties": {
"trainzMeshes": {
"title": "Trainz Meshes",
"description": "Specifications of Trainz meshes to create as FBX files and comments specified as an array.",
"type": "array",
"minItems": 1,
"items": [
{
"$ref": "#/$defs/trainzMesh"
}
],
"additionalItems": {
"type": "object",
"additionalProperties": false,
"properties": {
"comment": {
"$ref": "#/$defs/comment"
}
}
}
}
}
Upvotes: 0