I'm trying to learn JSON schema and choose the web validator to validate some examples. It works fine except when I try to reference another schema. The schemas are saved in my machine and I don't know how to make it works, or even if this kind of operation works in web validator.
When I try to use the validator with reference, I get the following error:
Error parsing schema
Message: Error when resolving schema reference 'file:///D:/schema/address.schema.json'. Path 'properties.shipping_address', line 12, position 25.
[![error image][1]][1]
[1]: https://i.stack.imgur.com/6z2pY.png
Any help will be appreciated!
The schemas and JSON used are show below:
{
"$id": "D:/schema/customer.schema.json",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"shipping_address": {
"$ref": "D:/schema/address.schema.json"
},
"billing_address": {
"$ref": "D:/schema/address.schema.json"
}
},
"required": [
"first_name",
"last_name",
"shipping_address",
"billing_address"
]
}
---
{
"$id": "D:/schema/address.schema.json",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
}
},
"required": [
"street_address",
"city",
"state"
]
}
--- JSON
{
"first_name": "first",
"last_name": "last",
"shipping_address": {
"street_address": "street",
"city": "city",
"state": "ST"
},
"billing_address": {
"street_address": "street",
"city": "city",
"state": "ST"
}
}
Related
Lets say I have two schemas defined as follows -
ADDRESS_CLASS_SCHEMA_DEFINITION = {
"title": "Address",
"type": "object",
"properties": {
"country_code": {
"$ref": "#/definitions/CountryCode"
},
"city_code": {
"title": "City Code",
"type": "string"
},
"zipcode": {
"title": "Zipcode",
"type": "string"
},
"address_str": {
"title": "Address Str",
"type": "string"
}
},
"required": [
"country_code",
"city_code",
"zipcode"
],
"definitions": {
"CountryCode": {
"title": "CountryCode",
"description": "An enumeration.",
"enum": [
"CA",
"USA",
"UK"
],
"type": "string"
}
}
}
EMPLOYEE_CLASS_SCHEMA_DEFINITION = {
"title": "Employee",
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
},
"email": {
"title": "Email",
"type": "string"
},
"telephone": {
"title": "Telephone",
"type": "string"
},
"address": {
"$ref": "#/definitions/Address"
}
},
"required": [
"id",
"name",
"email"
],
"definitions": {
"Address": ADDRESS_CLASS_SCHEMA_DEFINITION
}
}
I'm trying to re-use sub-schema definitions by defining a constant and referencing them individually in definitions (for example address-schema is referenced through constant in employee-schema definition). This approach works for individual schemas, however there seems to be a json-pointer path issue for Employee schema - #/definitions/CountryCode wouldn't resolve in Employee schema. I was assuming that #/definitions/CountryCode would be a relative path on Address schema as its scope is defined on a sub-schema, but my understanding seems wrong. I can make it work by flattening out like below, however I donot want to take this route -
{
"title": "Employee",
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
},
"email": {
"title": "Email",
"type": "string"
},
"telephone": {
"title": "Telephone",
"type": "string"
},
"address": {
"$ref": "#/definitions/Address"
}
},
"required": [
"id",
"name",
"email"
],
"definitions": {
"CountryCode": {
"title": "CountryCode",
"description": "An enumeration.",
"enum": [
"CA",
"USA",
"UK"
],
"type": "string"
},
"Address": {
"title": "Address",
"type": "object",
"properties": {
"country_code": {
"$ref": "#/definitions/CountryCode"
},
"city_code": {
"title": "City Code",
"type": "string"
},
"zipcode": {
"title": "Zipcode",
"type": "string"
},
"address_str": {
"title": "Address Str",
"type": "string"
}
},
"required": [
"country_code",
"city_code",
"zipcode"
]
}
}
}
I'm wondering how to fix this, I've briefly looked into jsonschema-bundling and using $id but from best practices it seems like the general recommendation is to use $id when dealing with URI's alone. Would like to know about best practices and how to fix this problem, would also appreciate if someone can point me on how to use $id correctly (for example, constant based approach seems to work when I provide identifiers like $id: Address, $id: Employee). Thanks in advance.
JSON Schema implementations work in JSON land. When you combine your schemas in your example above, presumably in javascript/node.js, by the time it gets to the JSON Schema implementation for validation execution, any knowledge that there were separate schemas is lost. (It's generally not considered that this approach is the best approach.)
The EASY fix here SHOULD be just to define $id in each of the roots of your schemas. These should be a fully qualfied URI. It doesn't really matter what they are at this point. They could be https://example.com/a and https://example.com/b. Then, in the primary schema, you can do $ref: https://example.com/b.
Implementations should provide you with a way to load in your other/non-primary schemas so the $id values can be stored in an index. Using $id in your other schema with a fully qualified URI will signify a "resource boundary".
https://json-schema.hyperjump.io is the only web playground to support multiple files/schemas/"Schema Resources", so you can test this out there to confirm your expectations.
Not all implementations make it easy or even provide a means to import your other schemas, but they should.
If you have follow up questions, feel free to leave a comment, or join the JSON Schema slack server if it would be off-topic for StackOverflow.
My local PouchDB docs won't replicate to my remote CouchDB.
The sync is happening, because the browser downloaded my design schema, so it's not a permissions issue. I think that my design schema doesn't match my documents, but I struggled to find the correct way to write the schema.
Schema
{
"_id": "_design/schema",
"_rev": "4-3d9a49ebffbbd6b7b146240879baa7e4",
"validate_doc_update": "function(newDoc, oldDoc, userCtx, secObj){ if(userCtx.roles[0] !== 'admin'){throw({forbidden: 'operation forbidden'})} }",
"views": {
"by_module": {
"map": "function(doc){ if(doc.type == 'note'){emit(doc.note);} }"
}
},
"schema": {
"title": "Contact details",
"description": "A document containing a person's contact details.",
"type": "object",
"required": [
"name",
"level"
],
"properties": {
"_id": {
"type": "string"
},
"_rev": {
"type": "string"
},
"application_access": {
"type": "string"
},
"home": {
"type": "string"
},
"home_email": {
"type": "string"
},
"jobtitle": {
"type": "string"
},
"level": {
"type": "string"
},
"mobile1": {
"type": "string"
},
"mobile2": {
"type": "string"
},
"modified": {
"type": "number"
},
"name": {
"type": "string"
},
"work": {
"type": "string"
},
"work_email": {
"type": "string"
},
"_doc_id_rev": {
"type": "string"
}
}
}
}
Doc
{
"_id": "fcb52b3072e2038647b328c0a700147f",
"_rev": "1518449239461",
"application_access": "User",
"home": "",
"home_email": "",
"jobtitle": "Exemplar",
"level": "Bronze",
"mobile1": "0987654321",
"mobile2": "",
"modified": 1518449239461,
"name": "Zachary Zumbeispiel",
"work": "",
"work_email": "",
"_doc_id_rev": "1518449239460::1-ffd3c056614845ada4a68de4793710ac"
}
So the question is, does my doc conform to my schema? Or is my schema wrong?
The "schema" was on the CouchDB instance, and the doc was in PouchDB.
They are replicating now, because I removed the "document schema" and so it can sync & replicate.
So the problem was that I had read somewhere that I needed to add a schema to CouchDB, so I created what I thought was a schema, and evidently the structure of the docs to be synced has to be the same, so the sync did not work because the structure of the docs did not match.
Hence #Flimzy 's explanation that the whole notion of a schema was bobbins meant that I removed the "schema", and voila, the PouchDB and CouchDB can now sync - and problem solved.
I'm using mule validate JSON schema component to validate my incoming json request.
It validates the type but not the required field attributes
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema",
"properties": {
"Employees": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"BirthDate": {
"type": "string",
"format": "date-time"
},
"EmpNum": {
"type": "number"
},
"FirstName": {
"type": "string"
},
"Gender": {
"type": "string"
},
"LastName": {
"type": "string"
},
"LicenseNumber": {
"type": "string"
},
"ZipCode": {
"type": "string"
}
},
"required": ["EmpNum", "LastName", "FirstName", "Street", "ZipCode", "BirthDate" ]
}
}
}
}
I have json as shown below:
{
"Employees": [
{
"EmpNum": 3,
"FirstName": "Finder",
"LastName": "Path",
"Street": "392 CDI CDIJUW",
"ZipCode": "12345",
"BirthDate": "1943-05-19T04:00:00Z",
"Gender": "M"
},
{
"EmpNum": 3,
"FirstName": "",
"LastName": "Path",
"Street": "392 CDI CDIJUW",
"ZipCode": "12345",
"BirthDate": "1943-05-19T04:00:00Z",
"Gender": "M"
}
]
}
Even though I have set a field to an empty string, it still takes as a valid request and proceeds further.
If you intentionally set FirstName to be an empty string and want to invalidate it, try adding minLength:
"FirstName": {
"type": "string",
"minLength": 1
},
With this schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"b": {
"type": "object",
"properties": {
"c": {
"type": "string"
}
}
},
"type": "object",
"properties": {
"a": {
"$ref": "#/b"
}
}
}
I can validate this example:
{
"a": {
"c": "test"
}
}
Now I want to create a new schema file for the "b" element and refer it in my 1st schema. How can I do this ? I try a lot of things but I always obtain jsonspec.reference.exceptions.NotFound: u'b.json' not registered.
After a couple of hours of googling and various doc readings, I've finally managed to be able to compose two json-schema's using "$ref" : "file:...".
My example data looks like this:
john-doe.json:
{
"first_name": "john",
"last_name": "doe",
"age": 42,
"address": {
"street_address": "foo street 42",
"city": "baklavastan",
"state": "foobar"
"foo": 42
}
}
And then I have two json schema files, which lives in the same directory on my file system.
customer.json
{
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"age" : { "type" : "integer" },
"address" : { "$ref" : "file:address.json#" }
},
"required": ["first_name", "last_name", "age", "address"],
"additionalProperties": false
}
address.json
{
"type": "object",
"properties": {
"street_address": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" }
},
"required": ["street_address", "city", "state"],
"additionalProperties": false
}
I am able to validate against the schema in Clojure using the library scjsv (which is a wrapper around the Java json-schema-validation library)
It gives me following validation error (in clojure edn) for the john-doe.json example:
{ :level "error",
:schema {:loadingURI "file:address.json#", :pointer ""},
:instance {:pointer "/address"},
:domain "validation",
:keyword "additionalProperties",
:message
"object instance has properties which are not allowed by the schema: [\"foo\"]",
:unwanted ["foo"] }
I have defined a customer object type in my json schema:
"customer": {
"type": "object",
"properties": {
"id": { "type": "string" },
"first_name": { "type": "string" },
"last_name": { "type": "string"},
"email": { "type": "string" },
"billing_address": { "$ref": "#\/definitions\/street_address" },
"shipping_address": { "$ref": "#\/definitions\/street_address" },
},
"required": [ "id", "first_name", "last_name", "email", "billing_address"]
},
I would like to validate shipping_address (optional object) if it is sent and reject it if it is missing the required fields. Here is the street_address object definition:
"street_address": {
"type": "object",
"properties": {
"first_name": {"type": "string" },
"last_name": { "type": "string" },
"address": { "type": "string" },
"address2": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip_code": { "type": "string"},
"country_code": { "type": "string"},
"phone": { "type": "string"},
"fax": {"type": "string"}
},
"required": [
"first_name",
"last_name",
"address",
"city",
"state",
"zip_code",
"country_code"
]
},
How do I configure my JSON schema to accomplish this? When I send a shipping address now, the fields inside the object do not get validated.
You are using the reference "$ref": "#\/definitions\/street_address" (btw, you dont have to escape the slashes). In that case the street_address definition must be in in the same document, inside the "defintions" node. So your schema file looks like this
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "customer",
"type": "object",
"properties": {
"id": { "type": "string" },
"first_name": { "type": "string" },
"last_name": { "type": "string"},
"email": { "type": "string" },
"billing_address": { "$ref": "#/definitions/street_address" },
"shipping_address": { "$ref": "#/definitions/street_address" },
},
"required": [ "id", "first_name", "last_name", "email", "billing_address"],
"definitions" : {
"street_address" : {
/* here comes the street_address definition */
},
/* other entity definitions */
}
}
I'm using the nodejs module jayschema (see https://github.com/natesilva/jayschema) for validation, and it works fine that way.