Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON Schema: multiple $ref get removed when reading file with require

I have a json schema defining several properties. I've moved 2 of the properties to definitions and I make references to them. I did this because I wanted to group them together and do some testing for these properties in a generic way. This works fine and all the json data is handled as before.

But, I noticed that when I read the json schema file into my javascript file, I only see the last $ref. I don't know what the cause of this is. I'd really need to know all of the properties that are referenced.

Here's an snippet of my json schema (in file schemas/schema1.json):

{
    "type": "object",
    "properties": {
         "$ref": "#/definitions/groupedProperties/property1",
         "$ref": "#/definitions/groupedProperties/property2"
    },
    "definitions": {
        "groupedProperties": {
            "type": "object",
            "properties": {
                "property1": {
                    "type": "string"
                },
                "property2": {
                    "type": "string"
                }
            }
        }
    }
}

Then I'm reading it into my js file like this (in file test.js):

var schemas = requireDir('./schemas')
for (var prop in schemas['schema1'].properties) {
    console.log(prop)
}

When I iterate over the properties in the schema from my js file, all I can see is one $ref. I imagine this is because it thinks the property name is '$ref' and there can be only unique names. Is there a certain way I need to require this file so that the first $ref doesn't get clobbered?

EDIT: My syntax wasn't passing the json schema validators, although I'm not sure why, so instead of struggling with that, I decided to do it a bit differently. All I wanted was a way to group certain properties, so I put the properties back in the main schema, and changed the definition to be just an enum of the property names comprising the group. So now my schema looks like:

{
    "type": "object",
    "properties": {
        "property1": {
            "type": "string"
        },
        "property2": {
            "type": "string"
        }
    },
    "definitions": {
        "groupedProperties": {
            "enum": ["property1", "property2"]
        }
    }
}

And then in my js file:

var myGroup = (schema.definitions ? schema.definitions.groupedProperties : [])
console.log(myGroup.enum) // [ 'property1', 'property2' ]
like image 441
kim Avatar asked Jun 23 '15 14:06

kim


People also ask

What does $Ref mean in JSON schema?

In a JSON schema, a $ref keyword is a JSON Pointer to a schema, or a type or property in a schema. A JSON pointer takes the form of A # B in which: A is the relative path from the current schema to a target schema. If A is empty, the reference is to a type or property in the same schema, an in-schema reference.

What is required in JSON schema?

In JSON, the “keys” must always be strings. Each of these pairs is conventionally referred to as a “property”. In Python, "objects" are analogous to the dict type. An important difference, however, is that while Python dictionaries may use anything hashable as a key, in JSON all the keys must be strings.

What does Exclusiveminimum property in JSON schema mean?

Gets or sets a flag indicating whether the value can not equal the number defined by the minimum attribute (Minimum).

Can JSON schema reference another schema?

A schema can reference another schema using the $ref keyword. The value of $ref is a URI-reference that is resolved against the schema's Base URI. When evaluating a $ref , an implementation uses the resolved identifier to retrieve the referenced schema and applies that schema to the instance.


1 Answers

There are a lot of problems with how you reference your definitions.

###JSON objects can not have duplicate properties All properties in a JSON or JavaScript object are unique. The second one will overwrite the first. Consider the syntax for accessing a property to understand why. When you read your JSON into a JavaScript object, you could try accessing the $ref property using schema.properties['$ref']. If there were two, which one (or both) would you get? JavaScript has no mechanism to distinguish because it is not allowed.

###$ref must stand alone When $ref is used in an object, it must be the only property in that object. All other properties will be ignored. This is just one more reason why having two $refs doesn't work.

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

###$ref should not be used in properties $ref should only be used to reference schemas. In this case, the properties keyword is using $ref which is an object with schema values. Using $ref in this way is not explicitly forbidden in the documentation for JSON Schema or JSON Reference, but it is not idiomatic JSON Schema and is consequently not supported by most validators. Even if the validator you are using does support references like this, it should be avoid because it is never necessary and can make the schema confusing and difficult to maintain.

###Your JSON-Pointers are wrong Your JSON-Pointers do not actually point to the schemas you have defined. The correct pointer would be #/definitions/groupedProperties/properties/property1.

###Posible Solutions This is what you were trying to do.

{
   "type": "object",
   "properties": {
        "property1": { "$ref": "#/definitions/groupedProperties/properties/property1" },
        "property2": { "$ref": "#/definitions/groupedProperties/properties/property2" }
   },
   "definitions": {
       "groupedProperties": {
           "type": "object",
           "properties": {
               "property1": {
                   "type": "string"
               },
               "property2": {
                   "type": "string"
               }
           }
       }
   }
}

Here is a cleaner way to include all of your groupedProperties at once.

{
    "type": "object",
    "allOf": [
        { "$ref": "#/definitions/groupedProperties" }
    ],
    "definitions": {
        "groupedProperties": {
            "type": "object",
            "properties": {
                "property1": {
                    "type": "string"
                },
                "property2": {
                    "type": "string"
                }
            }
        }
    }
}

Or, since you are only using it for testing purposes, you can flip it around so the definition references the schema. You can use the definition in your tests without it affecting your schema.

{
    "type": "object",
    "properties": {
        "property1": { "type": "string" },
        "property2": { "type": "string" }
    },
    "definitions": {
        "groupedProperties": {
            "type": "object",
            "properties": {
                "property1": { "$ref": "#/properties/property1" },
                "property2": { "$ref": "#/properties/property2" }
            }
        }
    }
}
like image 61
Jason Desrosiers Avatar answered Nov 04 '22 11:11

Jason Desrosiers