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

3.6k Views Asked by At

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' ]
2

There are 2 best solutions below

3
On BEST ANSWER

This has nothing to do with require, object keys are not unique (in that you may declare them several times in one object), but they are overwritable (in the same way that a variable declared twice is overwritable). You will only receive the last value declared on two keys with the same name.

I'd suggesting giving the refs a distinguishing ID, this will also aid clarity when your code expands

6
On

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.

###$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" }
            }
        }
    }
}