jsonschema pointer to data pointer - javascript

I have the following jsonSchema: (note that there are fields called 'properties' and its not 'json-schema peoperties(who represent fields)'
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"image": {
"type": "string",
"media": {
"binaryEncoding": "base64",
"type": "image/jpeg"
}
},
"properties": {
"type": "string"
},
"nameObj": {
"type": "object",
"properties": {
"properties": {
"type": "string"
},
"firstName": {
"title": "First Name",
"type": "string",
"maxLength": 100
}
}
}
}
}
data looks like:
{
"name": "person1",
"properties": "myProperties",
"nameObj": {
"properties": "nameProperties",
"firstName:": "myPerson"
}
}
I have dotnotation path to field "firstName" under the schema:
properties.nameObj.properties.firstName
and I want to convert it to data path as below:
nameObj.firstName
I cant only ignore 'properties' fields because (as you can see in above schema) 'properties' can be a data field name or a json-schema property.
JaveScript example will be greate.
Thanks

Ive crated a function to convert this path:
function schemaPathToEntityPath(schema,path) {
let fields = path.split('.');
let dataFields;
for (let field of fields) {
if (field === 'properties') {
//if its json-schema property
if (schema.hasOwnProperty('type') && schema.type === 'object') {
//go next level inside schema
schema = schema[field];
continue;
}
}
//if field actually exist in schema
if(schema.hasOwnProperty(field)) {
//go next level inside schema
schema = schema[field];
//for the first time just add field, later add '.' + field
dataFields = dataFields ? dataFields += '.' + field : field;
}
}
return dataFields;
}
example:
let schema={
"type": "object",
"properties": {
"name": {
"type": "string"
},
"image": {
"type": "string",
"media": {
"binaryEncoding": "base64",
"type": "image/jpeg"
}
},
"properties": {
"type": "string"
},
"nameObj": {
"type": "object",
"properties": {
"properties": {
"type": "string"
},
"firstName": {
"type": "string",
"maxLength": 100
}
}
}
}
}
let schemaPath1='properties.nameObj.properties.firstName'
let schemaPath2='properties.nameObj.properties.properties'
console.log(schemaPathToEntityPath(schema,schemaPath1));
//output= nameObj.firstName
console.log(schemaPathToEntityPath(schema,schemaPath2));
//output= nameObj.properties

Related

How to declare schema for at least one property is not null using JSON Schema?

const personData= {
name: null,
email: 'test#gmail.com'
}
const schema = {
instance: personData,
schema: {
type: "object",
anyOf: [
{ required: ["name", "email"] }
]
}
}
I want a schema which will validate the object and from object any of the key value (name or email) where one of them must be not null.
It looks like you're confused between "undefined" and "null", which are distinctly different. (I've now edited your question in light of your comment on my answer.)
The required keyword makes sure that a key is "defined" in the applicable object. The value is irrelevant, and may be null.
If you want to define the TYPE of a property, you have to use the type keyword.
anyOf must be an array of schemas where at least one of them must be true.
You've defined one subschema in the anyOf, and as a result, it must be true as a whole, making both items in the required array, required.
You want to define multiple schemas under your anyOf, where one each schema defines that a property must be of a specific type (null is a type).
{
"type": "object",
"required": ["name", "email"],
"anyOf": [
{
"properties": {
"name": {
"type": "string"
}
}
}, {
"properties": {
"email": {
"type": "string"
}
}
}
]
}
You can solve this by placing the 'required' property under the anyof list
{
"type": "object",
"anyOf": [
{
"required": ["name"],
"properties": {
"name": {
"type": "string"
}
}
}, {
"required": ["email"],
"properties": {
"email": {
"type": "string"
}
}
}
]
}
The required attribute means you must have a property in data. It may be nullable, though. To exclude null values make sure that anyOf properties you need are not null:
"anyOf": [
{
"properties": {
"name": {
"not": {
"type": "null"
}
}
}
},
{
"properties": {
"email": {
"not": {
"type": "null"
}
}
}
}
]
Full version goes below:
{
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
]
},
"email": {
"type": [
"string",
"null"
]
}
},
"required": [
"name",
"email"
],
"anyOf": [
{
"properties": {
"name": {
"not": {
"type": "null"
}
}
}
},
{
"properties": {
"email": {
"not": {
"type": "null"
}
}
}
}
]
}

Deriving the list of JSON Paths from a JSON Schema model

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)
);

AJV is not validating my schema

I am facing problem regarding AJV Schema Validator.
I have following schema
{
"$id": "create-offer.json#",
"body": {
"type": "object",
"properties": {
"statusCode": {
"type": "number"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"status": {
"type": "string"
},
"type": {
"type": "string"
},
"routePlanId": {
"type": "string"
},
"currencyId": {
"type": "string"
},
"autoRateUpdateActive": {
"type": "boolean"
}
}
}
}
And my response is :
{ statusCode: 2006,
statusPhrase: 'Error: ORA-00001: unique constraint (SPHERE_D1.CHECK_UNIQUE_RATE_NAME) violated\nORA-06512: at "SPHERE_D1.PKG_RATE_TABLES_V2", line 102\nORA-06512: at "SPHERE_D1.PKG_RATE_TABLES_V2", line 54\nORA-06512: at line 1' }
Using the following code to validate :
let valid = ajv.validate(schema, res);
var detailedErrorMsg = "\n" + ajv.errorsText(ajv.errors, { separator: "\n" }) + "\n";
console.log(detailedErrorMsg);
AJV should return error as schema and response are different, but AJV is returning 'no errors'.
Is there any problem with the code ?
This is resolved by adding required feilds in schema definition.
{
"$id": "create-offer.json#",
"description": "",
"title": "",
"type": "object",
"required": [
/*mention objects which should be requird*/
]
}

Schema <[object Object]> already exists with different definition

Getting Schema <[object Object]> already exists with different definition error while trying to refer 2 schemas in one.
Please correct me if am doing something wrong:
Coupons Schema in coupons.js
const COUPONS_SCHEMA = {
"id": "/Coupons",
"items": {
"id": "/items",
"properties": {
"Description": {
"type": "string"
},
"Ean": {
"type": "string"
},
"ExpiryDate": {
"type": "string"
},
"Id": {
"type": "string"
},
"Name": {
"type": "string"
},
"StartDate": {
"type": "string"
},
"Type": {
"type": "string"
},
"VoucherValue": {
"type": "string"
}
},
"type": "object"
},
"type": "array"
};
export default COUPONS_SCHEMA;
Rewards Schema in rewards.js
const REWARDS_SCHEMA = {
"id": "/Rewards",
"items": {
"id": "/items",
"properties": {
"PromotionId": {
"type": "string"
},
"Reward Amount": {
"type": "string"
},
"RewardType": {
"type": "string"
}
},
"type": "object"
},
"type": "array"
};
export default REWARDS_SCHEMA;
Am referencing above defined schemas in Discounts Schema
import { Validator } from 'jsonschema';
import Coupons from './coupons';
import Rewards from './rewards';
let validator = new Validator();
const DISCOUNTS_SCHEMA = {
"id": "/Discounts",
"properties": {
"Coupons": {
"$ref": "/Coupons"
},
"PromotionalClubCardPoints": {
"type": "string"
},
"Rewards": {
"$ref": "/Rewards"
},
"StaffDiscount": {
"type": "string"
},
"StandardClubCardPoints": {
"type": "string"
},
"TotalClubCardPoints": {
"type": "string"
},
"TotalCoupons": {
"type": "string"
},
"TotalGiftCards": {
"type": "string"
},
"TotalGreenClubCardPoints": {
"type": "string"
},
"TotalSavings": {
"type": "string"
},
"TotalVouchers": {
"type": "string"
}
},
"type": "object"
};
validator.addSchema(Coupons,'/Discounts');
validator.addSchema(Rewards,'/Discounts');
export default DISCOUNTS_SCHEMA;
and getting the below error
throw new Error('Schema <'+schema+'> already exists with different definition');
^
Error: Schema <[object Object]> already exists with different definition
at Validator.addSubSchema (/Users/repo/node_modules/jsonschema/lib/validator.js:72:15)
at Validator.addSubSchemaArray (/Users/repo/node_modules/jsonschema/lib/validator.js:99:10)
at Validator.addSubSchema (/Users/repo/node_modules/jsonschema/lib/validator.js:80:8)
at Validator.addSchema (/Users/repo/node_modules/jsonschema/lib/validator.js:48:8)
at Object.<anonymous> (/Users/repo/src/schema/discounts.js:47:11)
at Module._compile (module.js:570:32)
at loader (/Users/repo/node_modules/babel-register/lib/node.js:144:5)
at Object.require.extensions.(anonymous function) [as .js] (/Users/repo/node_modules/babel-register/lib/node.js:154:7)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
Please correct me if am doing something wrong in defining schemas.
The problem is probably that you use the id "/items" in both coupons.js and rewards.js. ids need to be universally unique. That is why they are supposed to be absolute URIs.
In my case (anyone else landing here to find similar issue),
we had a schema file in this format:
{
"keyA": {
"type": "string",
"example": "0319739002",
"id": "/keyA",
"minLength": 1,
"maxLength": 100
},
"keyB": {
"keyA": {
"type": "string",
"example": "0186013001",
"id": "/keyA",
"minLength": 1,
"maxLength": 100
}
}
}
where keyA was a part of keyB, but the id was repeated. Removing (/renaming) the 'id' field worked for us.

Convert JSON Schema Format

I have json schema definition like this:
(Update: basically its draft 03 format: http://json-schema.org/draft-03/schema#)
{
"$schema": "http://json-schema.org/draft-03/schema",
"product": {
"name": {
"required": true,
"type": "string"
},
"description": {
"required": true,
"type": "string"
}
},
"type": "object"
}
But I need it in this format(standard json schema structure), which is draft 04 format(http://json-schema.org/draft-04/schema#)
{
"type": "object",
"properties": {
"product": {
"type": "object",
"required": [
"name",
"description"
],
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
},
"required": [
"product"
]
}
Is there any converter to convert the above format to this one? I just don't want to manually do it which might be error prone.
I haven't used it, so I can't vouch for it personally, but there is conversion tool.
https://github.com/geraintluff/json-schema-compatibility

Categories

Resources