13

For example a schema for a file system, directory contains a list of files. The schema consists of the specification of file, next a sub type "image" and another one "text".

At the bottom there is the main directory schema. Directory has a property content which is an array of items that should be sub types of file.

Basically what I am looking for is a way to tell the validator to look up the value of a "$ref" from a property in the json object being validated.

Example json:

{
    "name":"A directory",
    "content":[
        {
            "fileType":"http://x.y.z/fs-schema.json#definitions/image",
            "name":"an-image.png",
            "width":1024,
            "height":800
        }
        {
            "fileType":"http://x.y.z/fs-schema.json#definitions/text",
            "name":"readme.txt",
            "lineCount":101
        }
        {
            "fileType":"http://x.y.z/extended-fs-schema-video.json",
            "name":"demo.mp4",
            "hd":true
        }

    ]
}

The "pseudo" Schema note that "image" and "text" definitions are included in the same schema but they might be defined elsewhere

{
    "id": "http://x.y.z/fs-schema.json",
    "definitions": {
        "file": {
            "type": "object",
            "properties": {
                "name": { "type": "string" },
                "fileType": {
                    "type": "string",
                    "format": "uri"
                }
            }
        },
        "image": {
            "allOf": [
            { "$ref": "#definitions/file" },
            {
                "properties": {
                    "width": { "type": "integer" },
                    "height": { "type": "integer"}
                }
            }
            ]
        },
        "text": {
            "allOf": [
            { "$ref": "#definitions/file" },
            { "properties": { "lineCount": { "type": "integer"}}}
            ]
        }
    },
    "type": "object",
    "properties": {
        "name": { "type": "string"},
        "content": {
            "type": "array",
            "items": {
                "allOf": [
                { "$ref": "#definitions/file" },
                { *"$refFromProperty"*: "fileType" } // the magic thing
                ]
            }
        }
    }
}
redben
  • 5,578
  • 5
  • 47
  • 63
  • Need clarification. You want to enforce that all the files items in a directory fullfil certain properties that are defined when the directory data is defined (not at design time), Am I right? – jruizaranguren Oct 17 '13 at 06:17
  • I want it to pick the right definition for a sub type of file from the property fileType – redben Oct 17 '13 at 11:43

2 Answers2

10

The validation parts of JSON Schema alone cannot do this - it represents a fixed structure. What you want requires resolving/referencing schemas at validation-time.

However, you can express this using JSON Hyper-Schema, and a rel="describedby" link:

{
    "title": "Directory entry",
    "type": "object",
    "properties": {
        "fileType": {"type": "string", "format": "uri"}
    },
    "links": [{
        "rel": "describedby",
        "href": "{+fileType}"
    }]
}

So here, it takes the value from "fileType" and uses it to calculate a link with relation "describedby" - which means "the schema at this location also describes the current data".

The problem is that most validators do not take any notice of any links (including "describedby" ones). You need to find a "hyper-validator" that does.

UPDATE: the tv4 library has added this as a feature

cloudfeet
  • 12,156
  • 1
  • 56
  • 57
  • Thanks, this seems to do the trick, though I still have to find the "hyper-validator" as you say, or roll my own ! – redben Oct 17 '13 at 22:29
  • Thanks cloudfeet! That is the answer I am looking for. describedBy let's me maintain a decoupled schema. I am using interpolation to calculate the URIs and I assume I can also use interpolation for the describedBy rel's href value, as you show above. Thanks for posting an answer! – Brian P Johnson Dec 29 '13 at 04:35
  • Do you know of any validators, for JS, that do support this? – Tavis Rudd Jul 10 '14 at 06:50
  • I believe that kind of thing was one of the goals of [this project](https://github.com/ericgj/json-schema-suite), but I don't know what state it's in. On the other hand, if you raise an issue on the [tv4 GitHub page](https://github.com/geraintluff/tv4), there's a good chance it'll get put in. – cloudfeet Jul 10 '14 at 16:55
3

I think cloudfeet answer is a valid solution. You could also use the same approach described here.

You would have a file object type which could be "anyOf" all the subtypes you want to define. You would use an enum in order to be able to reference and validate against each of the subtypes.

If the sub-types schemas are in the same Json-Schema file you don't need to reference the uri explicitly with the "$ref". A correct draft4 validator will find the enum value and will try to validate against that "subschema" in the Json-Schema tree.

In draft5 (in progress) a "switch" statement has been proposed, which will allow to express alternatives in a more explicit way.

Community
  • 1
  • 1
jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
  • 1
    Enum is not an option since I don't know all the possible sub-types at authoring time. I know "image" and "text" but someone might add a sub type of "file" in their own separate schema. In OOP that would map "file" would map to an interface or abstract api class. – redben Oct 17 '13 at 19:37
  • I don't see a problem with this. The one that extends the interface has informations of the additional enum values, and it can enforce them in the same way in OOP the one extending has knowledge of base interfaces and also the extensions. – jruizaranguren Oct 17 '13 at 20:52
  • I don't think I understand. If you use enum for the (fileType) property in the subtypes, ok but the schema would validate any value of "fileType" which is not desired... On the other hand "OOP the one extending has knowledge of base interfaces and also the extensions". No you don't have to know what other extensions are to make your own extension of a base abstract class or implement an interface :) – redben Oct 17 '13 at 22:26
  • I see different posibilities: base "class" restricts with "anyOf/oneOf" and derived classes restrict even more with another "anyOf/oneOf" to the enum types that are allowed. If you want base types to be inherited, then I would try to build a union of schemas dinamically at runtime. Is this feasible in your situation? P.D. I wanted to express "and own extensions" like you say. – jruizaranguren Oct 18 '13 at 06:16
  • Exactly, building a union schema as you say at runtime. This could be an option. Thanks ! – redben Oct 18 '13 at 11:13