Json Schema, take into account unspecified properties - json

Is there is any ways to get heads ups about properties which are not described by schema into the standard. For example this schema:
{
"description": "foo and bar",
"type": "object",
"properties": {
"foo": {
"type": "number"
},
"bar": {
"type": "string",
"enum": [
"a",
"b",
"c"
]
}
}
}
Will pass this JSON:
{
"foo": 12345,
"bar": "a",
"baz": 12312
}
Although it has baz property which is not the part of schema.
UPD: As of now I'm using tv4 library.

Option 1: additionalProperties in the schema
If you want to explicitly constrained to the properties listed in a particular properties definition, then you can use additionalProperties: false. This will restrict data to only using properties defined in the nearby properties entry - in this case, "foo" and "bar".
However, use of additionalProperties impacts schema extension. If you (or someone else) extends this schema later (using allOf/$ref), then the data will still be subject to this schema - so will only be allowed to use "foo" and "bar", regardless of definitions elsewhere.
Option 2: banUnknownProperties flag in tv4
If the kind of complete lock-down is not desired, then you can use the banUnknownProperties flag in tv4 (docs). Basically, after validation, any properties that were not accounted-for by any schemas are raised as errors.
It was designed for cases when extra properties (including future variations/extensions) are theoretically allowed in the data format, but you want (for testing/warning purposes) to be alerted when extra properties show up where you're not expecting them.

Related

JSON Schema object properties defined by enum

I'm attempting to reuse an enum in my JSON Schema to define the properties for an object.
I was wondering if the following is correct.
JSON Schema
{
"type": "object",
"propertyNames": {
"enum": ["Foo","Bar"]
},
"patternProperties": {
".*": {
"type": "number"
}
}
}
JSON Data
{
"Foo": 123,
"Bar": 456
}
The reason I ask is that I get inconsistent results from JSON Schema validation libraries. Some indicate the JSON validates, while others indicate the JSON is invalid.
p.s. if anyone is wondering "why" I'm trying to define the properties with an enum, it is because the enum is shared in various parts of my json schema. In some cases it is a constraint on a string, but I need the identical set of possible values both on those string properties and also on the object properties. As an enum I can maintain the set of possible values in one place.
Yes, that's a valid JSON Schema. You could also express it like this:
{
"type": "object",
"propertyNames": {
"enum": ["Foo","Bar"]
},
"additionalProperties": {
"type": "number"
}
}
It says "all property names must conform to this schema: (one of these values listed in the enum); also, all property values must conform to this schema: (must be numeric type)."
What errors do you get from the implementations that report this as invalid? Those implementations have a bug; would you consider reporting it to them?

Why are booleans valid JSON schemas?

I'm looking at JSON schema's meta schemas and according to the Core/Validation Dialect meta-schema the JSON true and false are valid JSON schemas. What is the point of this?
You can see that they validate properly here.
It's true that a boolean schema, in its entirety, is not terribly useful, except perhaps as a placeholder ("in the future, we will have a more defined data structure; in the meantime, all values are acceptable"). But schemas can also contain schemas, and then we can start to express more meaningful things.
Consider describing data thusly:
the top level of the data must be an object.
the object must include the properties "alpha" and "beta".
the value of "alpha" must be a string.
the value of "beta" can be anything.
the property "gamma" may exist, and its value can be anything.
the property "epsilon" must not be present.
if there any other properties present, they must be numbers.
We would define the JSON schema like this:
{
"type": "object",
"required": ["alpha", "beta"],
"properties": {
"alpha": {
"type": "string"
},
"beta": true,
"gamma": true,
"epsilon": false,
},
"additionalProperties": {
"type": "number"
}
}
Now, as you can see, under the "properties" and "additionalProperties" keywords we have several subschemas. Some of the subschemas contain more keywords ("type" in this case), but some of the subschemas are just plain old booleans. They're conveying something of meaning: "this condition is always valid" or "this condition is always invalid".

JSON schema reference another element in the document

Is there a way to express reference to another element in the same JSON document using JSON schema? The title might be a bit confusing, but i'm not looking for the "$ref" attribute, which references another type, but I'm curious, if there is a way, to reference another element in the document using a specified field. I know this is possible to enforce using xsd for xml documents, not sure about JSON.
I want to do something like this:
{
"people": [
{ "id": "1", "name": "A" },
{ "id": "2", "name": "B" },
{ "id": "3", "name": "C" }
],
"chosenOne": "1" // I want the schema to enforce a person ID here
}
I have been looking at the schema definition of v4: http://json-schema.org/draft-04/schema but didn't find anything, that looks like what I'm trying to do. Did I just miss it?
What you want is that you describe a reference ($ref) in the object your schema is describing.
kind of like this
{
"people": []
"chosenOne": { $ref: "#1"}
}
(or maybe a pointer if you want the value of the Id (https://json-spec.readthedocs.io/pointer.html)
I know of no direct way to do this but you might be able to use the pattern or oneof properties to force it being the right value. Kind of like this
"properties": {
"chosenOne"
"type": "string",
"oneOf": ["1","2","3"]
]
},
}
Similarly you could force the value of the property to be a reference pattern. That said since there is no reference value type (http://www.tutorialspoint.com/json/json_data_types.htm) only number or string you can't guarantee the meaning of the value. You can just guarantee that if follows some kind of reference pattern.
If you need more than what json schema's can give you you might want to look in odata for example. OData has some extra things so you can describe an entitySet and then define a navigation property to that set.
It does however force you to follow the odata structure so you aren't as free as you would be with a regular json schema.

JSON Schema regarding use of $ref

I understand that $ref takes a URI to a json schema to use but where does $ref : "#" point to?
Does it just mean use the current schema for this block level? Or does it mean to use the root level schema defined in the root level id?
Thanks
EDIT:
So if I have:
"items": {
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
}
Because it lacks an id field it will attempt to validate the instance items with the root schema first and then if that fails try to validate it with the schemaArray schema defined in the definitions schema, right?
So if I change it to:
"items": {
"id" : "#/items",
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
}
Then the first subschema in anyOf array will point to the items schema itself?
EDIT #2: Okay so if I had:
"items": {
"id" : "itemSchema",
"anyOf": [
{ "$ref": "#" },
{ "$ref": "#/definitions/schemaArray" }
],
"default": {}
}
and
"stringArray": {
"type": "array",
"items": { "$ref" : "itemSchema" },
"minItems": 1,
"uniqueItems": true
}
"stringArray"'s "items" field would be validated against the above "itemsSchema"?
Also does the second $ref in 'anyOf' work by going to the root and then traversing down the path till it hits that schema?
Thanks!
OK: each $ref is resolved into a full URI. Once that is done, all your questions are answered by asking the question: What schema would I end up with, if I simply fetched that URI? Where the $ref is, how it was loaded, all of that is irrelevant - it's entirely dependent on the resolved URI.
The library might take some shortcuts (like caching documents so they are only fetched once, or trusting one schema to "speak for" another), but those are all implementation details.
Response to original question:
# is not special: all values of $ref are resolved as URIs relative to the current document (or the closest value of "id", if there is one).
Therefore, if you haven't used "id", then # will point to the root of the schema document. If you fetched your schema from http://example.com/schema, then a {"$ref": "#"} anywhere inside that will resolve to http://example.com/schema#, which is the document itself.
It is different when you use "id", because it changes the "base" schema against which the $ref is resolved:
{
"type": "array",
"items": {
"id": "http://example.com/item-schema",
"type": "object",
"additionalProperties": {"$ref": "#"}
}
}
In that example, the $ref resolves to http://example.com/item-schema#. Now, if your JSON Schema setup trusts the schema it already has, then it can re-use the value from "items".
However, the point is there is nothing special about # - it just resolves to a URI like any other.
Response to EDIT 1:
Your first example is correct.
However, your second is unfortunately not. This is because of the way that fragments resolution works for URIs: one fragment completely replaces another. When you resolve the # against the "id" value of #/items, you don't end up with #/items again - you end up with #. So in your second example, the first entry in "anyOf" will still resolve to the root of the document, just as in the first example.
Response to EDIT 2:
Assuming the document is loaded from http://example.com/my-schema, the full URIs of your two $refs are:
http://example.com/itemSchema#
http://example.com/itemSchema#/definitions/schemaArray
For the first one, the library may use the schema it already has, but it might not - after all, looking at the URIs, http://example.com/my-schema might not be trusted to accurately represent http://example.com/itemSchema.
For the second one - that's not going to work, because the "itemSchema" doesn't have a "definitions" section, so that $ref won't resolve properly at all.

How can i define a single, unique Key-Valuepair in JsonSchema?

The Schema should allow only the following constellation: {"status":"nok"}.
The Key must always be "status" and the value should allow "ok","nok","inProgress"
No differen or additional objects,... should be allowed
I have tried this:
{
"description": "blabla",
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"ok",
"inProgress",
"nok"
],
"required": true,
"additionalItems": false
}
},
"required": true,
"additionalProperties": false
}
This works, but this scheme allows that i can send the same key/value pair twice like {"status":"nok","status":"nok"}
I would be also happy, if it would work without this "object"-container that i'm using, because to reduce overhead.
Maybe someone knows a solution, thanks
There is a more fundamental issue with that input:
{"status":"nok","status":"nok"}
mainly: that input is not valid JSON. RFC 4627, section 2.2, explicitly states that "The names within an object SHOULD be unique". And in your case, they are not.
Which means the JSON parser you use can do whatever it wants to with such an input. Some JSON APIs will grab whatever value they come upon first, other parsers will grab the last value they read, others will even coalesce values -- none of this is illegal as per the RFC.
In essence: given such input, you cannot guarantee what the output of the JSON parser will be; and as such, you cannot guarantee JSON Schema validation of such an input either.