Description
I'm using library react-json-schema to display JSON schema as a form.
I have a requirement, where the form is very large, hence I'm using uiSchema groups to form the tabs. But one of my attributes, which is an object, contains many objects. I want to spread these child objects further into the tabs.
I used the code suggested in #784 . I went through GroupedSchema code and it checks only in properties of schema, if the given field is present or not. It doesn't go deep down inside a nested object.
Steps to Reproduce
1.A dummy Schema:
{
"A": {
"type": "string",
"title": "A"
},
"B": {
"type": "string",
"title": "B"
},
"C": {
"type": "string",
"title": "C"
},
"D": {
"type": "object",
"properties": {
"DA": {
"type": "object",
"properties": {
"DAA": {
"type": "string",
"title": "DAA"
},
"DAB": {
"type": "string",
"title": "DAB"
}
}
},
"DB": {
"type": "object",
"properties": {
"DBA": {
"type": "string",
"title": "DBA"
},
"DBB": {
"type": "string",
"title": "DBB"
}
}
},
"DC": {
"type": "object",
"properties": {
"DCA": {
"type": "string",
"title": "DCA"
},
"DCB": {
"type": "string",
"title": "DCB"
}
}
}
}
}
}
Expected behavior
I'm trying to get tabs for A,B,C,DA,DB,DC
Here is the link to codesandbox
Related
I have json object whose property values are unique and can be anything;
{
"cat1": {
"name": "kitty",
"type": "animal",
"color": "ginger"
},
"dog2": {
"name": "ripple",
"type": "animal",
"color": "black"
},
"book10": {
"name": "myBook",
"type": "book",
"color": "NA"
},
"orange6": {
"name": "NA",
"type": "fruit",
"color": "orange"
},
"pig1":{
"name": "spring",
"type": "animal",
"color": "pink"
}
}
Now I'm confused how to write its validation schema. Does anybody know how to do it?
var mySchema = {
"type": "object",
"properties": {
// no idea how to check varying properties like cat1, dog2, etc. which might change next time
}
}
You can try this
var mySchema = {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"name": { "type": "string"},
"type": { "type": "string"},
"color": { "type": "string"},
}
}
}
ref: JSONSchema how to define a schema for a dynamic object
I am looking for a Javascript library to list the possible Json Paths based on a Json Schema.
For a json schema like below, I want to list the possible json paths.
{
"$id": "https://example.com/person.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Customer",
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "The person's first name."
},
"lastName": {
"type": "string",
"description": "The person's last name."
},
"age": {
"description": "Age in years which must be equal to or greater than zero.",
"type": "integer",
"minimum": 0
},
"address": {
"type": "object",
"city": {
"type": "string",
},
"country": {
"type": "string",
}
}
}
}
Possible Json Paths: firstName, lastName, age, address.city, and address.country
Based on #customcommander's answer
Add support for $ref (prevent recursion)
Add hierarchy parents paths too (i.e. a.b.c -> a + a.b + a.b.c)
Add support for array items (using [] as a generic indexer)
const _derivePathsFromSchema = (schema, properties, defined) => {
let paths = [];
if (!properties) return paths;
return Object.keys(properties).reduce((paths, childKey) => {
let child = properties[childKey];
const { $ref, ...childProperties } = child;
if ($ref?.startsWith('#/definitions/')) {
const definition = $ref.substr($ref.lastIndexOf('/') + 1);
if (!defined.includes(definition)) // prevent recursion of definitions
{
defined.push(definition);
child = {
...schema.definitions[definition], // load $ref properties
...childProperties, // child properties override those of the $ref
};
}
}
if (child.type === 'object') {
return paths.concat(childKey, _derivePathsFromSchema(schema, child.properties, defined.slice()).map(p => `${childKey}.${p}`));
}
if (child.type === 'array' && child.items?.properties) {
return paths.concat(childKey, `${childKey}[]`, _derivePathsFromSchema(schema, child.items.properties, defined.slice()).map(p => `${childKey}[].${p}`));
}
return paths.concat(childKey);
}, paths);
};
const derivePathsFromSchema = schema => _derivePathsFromSchema(schema, schema.properties, []);
console.log(derivePathsFromSchema({
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"BType": {
"type": "object",
"properties": {
"a": {
"$ref": "#/definitions/AType"
}
}
},
"AType": {
"type": "object",
"properties": {
"b": {
"$ref": "#/definitions/BType"
}
}
}
},
"type": "object",
"properties": {
"a": {
"$ref": "#/definitions/AType"
},
"b": {
"$ref": "#/definitions/BType"
},
"id": {
"type": "string"
},
"array": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item": { "type": "string" }
}
}
},
"obj": {
"type": "object",
"properties": {
"nested": { "type": "string" }
}
}
}
}));
You don't necessarily need a library for that. You can use a simple recursive function:
var schema = {
"$id": "https://example.com/person.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Customer",
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "The person's first name."
},
"lastName": {
"type": "string",
"description": "The person's last name."
},
"age": {
"description": "Age in years which must be equal to or greater than zero.",
"type": "integer",
"minimum": 0
},
"address": {
"type": "object",
"properties": {
"city": {
"type": "string",
},
"country": {
"type": "string",
}
}
}
}
};
var path = ({properties}) =>
Object.keys(properties).reduce((acc, key) =>
acc.concat(properties[key].type !== 'object' ? key :
path(properties[key]).map(p => `${key}.${p}`)), []);
console.log(
path(schema)
);
I have this schema for authors:
module.exports = {
"id": "Author",
"properties": {
"name": {
"type": "string",
"description": "The full name of the author"
},
"description": {
"type": "string",
"description": "A small bio of the author"
},
"books": {
"type": "array",
"description": "The list of books published on at least one of the stores by this author",
"items": {
"$ref": "Book"
}
},
"website": {
"type": "string",
"description": "The website url of the author"
},
"avatar": {
"type": "string",
"description": "The url of the avatar of this author"
}
}
}
When I POST to http://localhost:9000/authors/ to create a new author I get this error:
error: Error of type InternalServerError found: ValidationError: books: Cast to Array failed for value "[ 'The Twits', 'The BFG' ]" at path "books"
This is the JSON I'm posting
{
"name": "Roald Dahl",
"description": "Writes childrens novels",
"books": [
"The Twits",
"The BFG"
],
"website": "www.roalddahl.com",
"avatar": "https://www.natgeokids.com/wp-content/uploads/2016/11/Roald-Dahl-1-1.jpg"
}
To the best of my knowledge this JSON is correct. The error seems to suggest there is an issue with the books array. Is this the case and if so, how do I fix it?
Adding Book schema:
module.exports = {
"id": "Book",
"properties": {
"title": {
"type": "string",
"descrtiption": "The title of the book"
},
"authors": {
"type": "array",
"description": "List of authors of the book",
"items": {
"$ref": "Author"
}
},
"isbn_code": {
"type": "string",
"description": "The stores where clients can buy this book"
},
"stores": {
"type": "array",
"description": "The stores where clients can buy this book",
"items": {
"type": "object",
"properties": {
"store": {
"$ref": "Store"
},
"copies": {
"type": "integer"
}
}
}
},
"genre": {
"type": "string",
"description": "Genre of the book",
},
"description": {
"type": "string",
"description": "Description of the book"
},
"reviews": {
"type": "array",
"items": {
"$ref": "ClientReview"
}
},
"price": {
"type": "number",
"minimum": 0,
"description": "The price of this book"
}
}
}
You are posting JSON with the books field as an array of strings, so your validator object should specify:
items: { type: "string" },
I'm using Node 9.2.0 and ajv 6.0.0.
I have a schema that I wish to use negative lookbehind on, it's defined like:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "release format",
"description": "A Release Format",
"type": "object",
"properties": {
"id": {"type": "integer"},
"title": {
"anyOf": [
{
"type": "string",
"pattern": "^((?!itunes).)*$"
},
{
"type": "string",
"pattern": "^((?!exclusive).)*$"
},
{
"type": "string",
"pattern": "^((?!ringtone).)*$"
}
]
}
}
}
However, it appears that when I try and validate this with AJV using the following data: {"id": 123, "title": "world exclusive"} I don't get a validation error.
The code:
const Ajv = require('ajv');
class Validator {
constructor() {
this.releaseFormatSchema = JSON.parse(fs.readFileSync('./schemas/release-format.json'));
this.schemaValidator = new Ajv({allErrors: true});
}
validate(data) {
let validate = this.schemaValidator.compile(this.releaseFormatSchema);
let valid = validate(data);
console.log(valid);
console.log(validate);
}
}
where data would be: {"id": 123, "title": "world exclusive"}. I would expect this to error, however it's currently telling me that the data is valid.
The answer was also found by #sln and #ClasG, anyOf does a union between titles patterns can match : "all except strings which contains itunes" union "all except strings which contains exclusive" union "...", which means all which not contains all the forbidden keywords. It can be fixed either
using allOf instead of anyOF
"title": {
"allOf": [
{
"type": "string",
"pattern": "^((?!itunes).)*$"
},
{
"type": "string",
"pattern": "^((?!exclusive).)*$"
},
{
"type": "string",
"pattern": "^((?!ringtone).)*$"
}
]
}
using single type/pattern :
"title": {
"type": "string",
"pattern": "^((?!itunes|exclusive|ringtone).)*$"
}
I have write one schema to validate the response body. And set all the items as "required". But when the body return empty array, it till PASS, which supposed should be FAIL. Schema like this:
var schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array",
"items": {
"$ref": "#/definitions/MyObject"
},
"definitions": {
"MyObject": {
"type": ["object"],
"properties": {
"transactionId": "integer",
"transactionType": "string",
"bpCode": "string",
"bpId": "string",
"postingDate ": "string",
"dueDate": "string",
"totalAmount": "number",
"balanceDue": "number",
"reconcileAmount": "number",
"debitCredit": "string",
"remarks": "string",
},
"required": ["transactionId", "transactionType", "bpCode", "bpId", "postingDate", "dueDate", "totalAmount", "balanceDue", "reconcileAmount", "debitCredit", "remarks"],
"additionalProperties": false
}
}
};
tests["Valid respong body schema"] = tv4.validate(data.body, schema);
The response like this:
{
"errorCode": null,
"errorMessage": null,
"body": []
}
You should exclude the empty array with:
"type": "array",
"minItems": 1
"items": {
"$ref": "#/definitions/MyObject"
}