2

I have three json-schema definitons. client, address and contact.

client.json

{
  "$id": "client.json",
  "type": "object",
  "definitions": {},
  "$schema": "http://json-schema.org/draft-06/schema#",
  "properties": {
    "name": {
      "$id": "/properties/name",
      "type": "string"
    },
    "id": {
      "$id": "/properties/id",
      "type": "integer"
    },
    "contact": {
            "$ref": "contact.json"
    },
    "address": {
        "$ref": "address.json"
    }
  }
}

address.json

{
  "$id": "address.json",
  "type": "array",
  "definitions": {},
  "$schema": "http://json-schema.org/draft-06/schema#",
  "items": {
    "$id": "/items",
    "type": "object",
    "properties": {
      "addressId": {
        "$id": "/items/properties/addressId",
        "type": "integer"
      },
      "addressName": {
        "$id": "/items/properties/addressName",
        "type": "string"
      }
    }
  }
}

contact.json

{
  "$id": "contact.json",
  "type": "array",
  "definitions": {},
  "$schema": "http://json-schema.org/draft-06/schema#",
  "items": {
    "$id": "/items",
    "type": "object",
    "properties": {
      "contactId": {
        "$id": "/items/properties/contactId",
        "type": "integer"
      },
      "contactName": {
        "$id": "/items/properties/contactName",
        "type": "string"
      },
      "address": {
          "$ref": "address.json"
      }
    }
  }
}

object To be validated

var client = {
    "name": "test",
    "id": 12,
    "contact": [
        {
        "contactId": 12212,
        "contactName": "jon",
        "address": [
            {
                "addressId": 64,
                "addressName": "pi"
            }
        ]
    }
    ],
    "address": [
        {"addressId": 4242,
        "addressName": "doe"}
    ]
};

$ref's from 'client.json' work fine but I get an error on referring 'address.json' from 'contact.json'. I get no errors on using $refs inside of 'additionalItems' but fails to validate against schema pointed by $ref's.

I wish to know how do I use $ref from an array type schema definition. Also, I'm using AJV for schema validation.

Edit 1: AJV setup

var Ajv = require('ajv');
var ajv = new Ajv({
    $data: true,
    allErrors: true,
    useDefaults: true, 
    coerceTypes: true, 
});

ajv.addSchema(client);
ajv.addSchema(contact);
ajv.addSchema(address);

let valid = ajv.validate('client.json', payload);

if(!valid){
    console.log(ajv.errors);
}
Kshateesh
  • 571
  • 1
  • 8
  • 21

1 Answers1

6

I'm sure the problem is that $id changes the resolution scope of the $ref. I'm guessing $ref resolution is happening by looking for files on the file system. Let's assume your three schemas are available at file:///path/to/schema.

  1. You start processing the file:///path/to/schema/client.json schema.
  2. You encounter the reference contact.json. This is relative URI, so you need to determine the URI it is relative to in order to resolve it.
  3. You backtrack up the schema and you find the nearest $id with value client.json.
  4. This is a relative URI and there are no more $ids, so the file's path, file:///path/to/schema/client.json is used.
  5. You can now resolve client.json against file:///path/to/schema/client.json and get file:///path/to/schema/client.json.
  6. You can now resolve contact.json against file:///path/to/schema/client.json and get file://path/to/schema/contact.json.

Here's where it starts to get weird.

  1. You retrieve the file:///path/to/schema/contact.json schema.
  2. You encounter the reference address.json. This is a relative URI, so you need to determine the URI it is relative to in order to resolve it.
  3. You backtrack up the schema and you find the nearest $id with value /items.
  4. This is a relative URI, so you keep backtracking and find contact.json.
  5. This is a relative URI and there are no more $ids, so the file's path, file:///path/to/schema/contact.json is used.
  6. Now you can resolve /items against file:///path/to/schema/contact.json and get file:///items.
  7. Now you can resolve address.json against file:///items and get file:///address.json.
  8. You attempt to retrieve the file:///address.json schema, but it doesn't exist.

Because $id changes resolution scope of $ref, it's highly discouraged to give everything an $id like you have done in your schemas. This feature exists for use cases like combining multiple small schemas into one. You should really never use it other than at the root of the document unless you have a really good reason and understand the implications.

Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
  • Thank you! @Jason. $ref's work on removing items $id . I've been using online schema generators so $ids came with schemas itself and never thought of removing $ids considering them harmless. – Kshateesh Mar 27 '18 at 06:11
  • Check this answer too for better understanding how $ref works when id is used. https://stackoverflow.com/questions/17595377/json-schema-regarding-use-of-ref – Juliano Suman Curti Jun 20 '22 at 15:10