Reputation: 3391
Is there a standard approach to specifying a property to be a dictionary or map keyed by string with a value type T specified somewhere else in the schema?
For example, suppose you want to model a user's favorite movies where the key type is the name of the movie and the value type is some set of attributes about the movie (year made, budget, gross income, etc.)
I imagine you could model first a MovieDataPair as a type with name property and a value property containing the desired attributes. Then the map would be an array of those. But, then you would need a special unique constraint that ensured any movie name only appeared once.
Is there something in json schema to support this, or a standard pattern used for it? If not built in support in json schema, what about other schema solutions?
Upvotes: 2
Views: 3315
Reputation: 133
Here's my way to support for map. Hope to help.
{ "type": "object", "title": "map data", "required": [ "map" ], "properties": { "sOnePurRecord": { "title": "map", "additionalProperties": false, "properties": { "mapItem": { "type": "object", "maxProperties": 10, "minProperties": 1, "patternProperties": { "^[a-zA-Z0-9]{5,20}$": { "$ref": "#/definitions/value" } }, "additionalProperties": { "$ref": "#/definitions/value" } } }, "required": [ "mapItem" ] } }, "definitions": { "value": { "type": "object", "properties": { "name": { "type": "string" }, "id": { "type": "integer" } } } } }
Upvotes: 0
Reputation: 3391
After some study I've come up with the following answer:
The best way to see this in action is to find some examples. It happens that there are several examples of this in the draft04 schema itself (definitions, properties, patternProperties,...) and they usually follow the same pattern.
For example, the definitions property of the draft04 schema defines what should appear in a schema at the definitions property. Here is the subschema associated with the definitions property:
"definitions": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
This says the entry at "#/definitions/" must be an object. The fact that it is a json object means it will have unique keys itself. Now for the values in the object, that is what additionalProperties is designed to describe. In this case it says that the value of each property must itself conform to the root of the schema "#". What this means is that each value in the definitions property object of a valid json schema object must also be a schema. If this were typed like C++ it might look like:
std::map< std::string, Schema > definitions;
Effectively a map with a string key can be thought of as like a json object with a structured value type. So, to create your own:
std::map< std::string, T >
First define the schema for T. For example:
"definitions" : {
"movie" : {
"properties": {
"title" : { "type" : "string" },
"year_made" : { "type" : "integer" },
"rating" : { "type" : "integer" }
}
}
}
For the value type T stored, decide if you want to allow any properties, as long as these specified properties are typed as specified above. If you only want these properties, add "additionalProperties" : false
"definitions" : {
"movie" : {
"additionalProperties" : false,
"properties": {
"title" : { "type" : "string" },
"year_made" : { "type" : "integer" },
"rating" : { "type" : "integer" }
}
}
}
Also decide if you actually require all of the properties to be present for the movie to be valid. If so, add a required entry.
"definitions" : {
"movie" : {
"additionalProperties": false,
"required" : [ "title", "year_made", "rating" ],
"properties": {
"title" : { "type" : "string" },
"year_made" : { "type" : "integer" },
"rating" : { "type" : "integer" }
}
},
Now the shape T for movie is defined. Create a definition for the collection, or map of movies referencing the movie schema defined as was done by definitions in the draft schema. Note: in the "movie_map" additionalProperties has a different meaning than that of "movie". In the case of "movie" it is a boolean false which indicates no additional properties beyond what is listed in properties. In the case of "movie_map" it means - if there are additional properties, they must look like this schema. But, since no properties have been specified in movie_map it really means all properties in the object instance must conform to #/definitions/movie. Now all values in a "movie_map" will look like the defined movie schema.
{
"definitions" : {
"movie" : {
"additionalProperties": false,
"required" : [ "title", "year_made", "rating" ],
"properties": {
"title" : { "type" : "string" },
"year_made" : { "type" : "integer" },
"rating" : { "type" : "integer" }
}
},
"movie_map" : {
"type": "object",
"additionalProperties": { "$ref": "#/definitions/movie" },
"default": {}
}
}
}
Now use the defined schema movie_map somewhere within the schema:
{
"title" : "movie data",
"additionalProperties" : false,
"required" : [ "movies" ],
"properties" : {
"movies" : { "$ref" : "#/definitions/movie_map" }
},
"definitions" : {
"movie" : {
"additionalProperties": false,
"required" : [ "title", "year_made", "rating" ],
"properties": {
"title" : { "type" : "string" },
"year_made" : { "type" : "integer" },
"rating" : { "type" : "integer" }
}
},
"movie_map" : {
"type": "object",
"additionalProperties": { "$ref": "#/definitions/movie" },
"default": {}
}
}
}
Here is a sample object, which can be thought of as a map, of movies that validates against the schema:
{
"movies" : {
"the mission" : {
"title":"The Mission",
"year_made":1986,
"rating":5
},
"troll 2" : {
"title":"Troll 2",
"year_made":1990,
"rating":2
}
}
}
Upvotes: 5
Reputation: 13615
If I wanted to model a structure for users favorites movies (remind Json Schema is intended for structure validation) I would make something like:
{
"description":"moviesFan",
"properties": [
"favoriteMovies": {
"type":"array",
"uniqueItems":True
"allOf": [{ "$ref": "#/definitions/movie" }]
}
],
"definitions": {
"movie": {
"type": "object",
"properties": {
"yearMade": {}
...
}
}
}
Does it make sense to you?
Upvotes: 1