Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation

License

NotificationsYou must be signed in to change notification settings

sagold/json-schema-library

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Npm package versionCITypes

json-schema-library
json-schema-library

json-schema-library provides tools and utilities for working with JSON Schema - enabling creation, validation, and schema exploration. Unlike most validators and editors, which hide the inner workings, this library is designed for developers building custom tools around JSON Schema. It runs in both Node and browser environments, prioritizing flexibility and extensibility over minimal memory footprint or raw performance.



Quick start

npm install json-schema-library

json-schema-library includes a compileSchema function that converts a JSON Schema into a SchemaNode, which gives you easy-to-use methods for working with the schema.

import{compileSchema,SchemaNode}from"json-schema-library";importmyJsonSchemafrom"./myJsonSchema.json";importmyDatafrom"./myData.json";constschema:SchemaNode=compileSchema(myJsonSchema);// validate data and collect errors if invalidconst{ valid, errors}=schema.validate(myData);// create data which validates to the compiled JSON SchemaconstdefaultData=schema.getData();// access a subschema at a specific JSON Pointer locationconst{ node, error}=schema.getNode("#/image/title");node&&console.log(node.schema);

Per default,compileSchema uses the draft-version referenced in$schema forcross-draft support. In case $schema is omitted or invalid the latest schema (draft-2020-12) will be used. Customizing draft selection is documented indraft customization.

constschemaNode=compileSchema({$schema:"draft-07"});console.log(schemaNode.getDraftVersion());// draft-07

Overview

compileSchema

UsecompileSchema once to turn a JSON Schema into a tree of SchemaNodes. After that, you'll work with individual nodes in the tree. You can also pass an options object tocompileSchema to customize how the nodes are created.

typeCompileOptions={// set of drafts to usedrafts:Draft[];// a context to shareremote:SchemaNode;// if format-validations should create errors. Defaults to trueformatAssertion:boolean|"meta-schema";// default options for all calls to node.getData()getDataDefaultOptions?:{// Add all properties (required and optional) to the generated dataaddOptionalProps?:boolean;// Remove data that does not match input schema. Defaults to falseremoveInvalidData?:boolean;// Set to false to take default values as they are and not extend them. Defaults to trueextendDefaults?:boolean;// Limits how often a $ref should be followed before aborting. Prevents infinite data-structure. Defaults to 1recursionLimit?:number;};};

With this

import{compileSchema,draft04,draft06,draft07,draft2019,draft2020}from"json-schema-library";// only draft07 is used for all JSON schemacompileSchema(mySchema,{drafts:[draft07]});// the created node will share a context with `anotherSchemaNode` enabling cross schema ref-resolution// Note that anotherSchemaNode still uses its own drafts it was compiled withcompileSchema(mySchema,{remote:anotherSchemaNode});// format validation is disabledcompileSchema(mySchema,{formatAssertion:false});// for all calls to getData, `addOptionalProps` is `true` per defaultcompileSchema(mySchema,{getDataDefaultOptions:{addOptionalProps:true}});

Details ondrafts are documented indraft customization.Details ongetDataDefaultOptions are documented ingetData.

SchemaNode

compileSchema builds a tree where each sub-schema becomes its own SchemaNode. Every node in the tree offers the same set of methods.For example:

constroot=compileSchema(mySchema);constrootData=root.getData();const{node:titleNode}=root.getNode("#/image/title");consttitleData=titleNode?.getData();
Each node has an identity
consttitleNode=compileSchema(mySchema).getNode("#/image/title");console.log(titleNode.evaluationPath);// #/properties/image/properties/titleconsole.log(titleNode.schemaLocation);// #/properties/image/properties/title
  • evaluationPath refers to the path in schema and is extended by$ref, e.g. if image is defined on$defs:#/properties/image/$ref/properties/title
  • schemaLocation refers to the absolute path within the schema and will not change, e.g.#/$defs/properties/title
Each node has a reference to its parent node

The parent-node can be a sub-schema or intermediary node:

constroot=compileSchema(mySchema);const{node:childNode}=root.getNode("#/image");assert(root===childNode.parent);
All nodes share a context

It is not advised to work on context directly, but it might be useful in some situations

A context is shared across all nodes of a schema

constroot=compileSchema(mySchema);const{node:childNode}=root.getNode("#/image");assert(root.context===childNode.context);

And some context properties are shared across all schema added as remotes. The propertyrootNode refers to the root-schema for the current node

constroot=compileSchema(mySchema);const{node:childNode}=root.getNode("#/image");assert(root===childNode.context.rootNode);

Note that rootNodes will change when working across remote schema (using $ref).

Draft Support

json-schema-library fully supports all core features of draft versions draft-04, draft-06, draft-07, draft-2019-09 and draft-2020-12. Additionally, most format-validations are supported per default besides the listed format below. You can always override or extend format validation as is documented indraft customization.

Overview draft support

Draft support is defined by running a validator against the officialjson-schema-test-suite.

Please note that these benchmarks refer to validation only.json-schema-library offers tooling outside of validation and strives to be as spec-compliant as possible.

Overview format validation support
  • ❌ unsupported formats iri, iri-reference, idn-hostname
  • ✅ supported formats: date, date-time, date, duration, ecmascript-regex, email, hostname, idn-email, ipv4, ipv6, json-pointer, regex, relative-json-pointer, time, unknown, uri-reference, uri-template, uri, uuid

SchemaNode methods

addRemoteSchema ·compileSchema ·createSchema ·getChildSelection ·getData ·getNode ·getNodeChild ·getNodeRef ·getNodeRoot ·reduceNode ·toDataNodes ·toSchemaNodes ·validate

addRemoteSchema

addRemoteSchema lets you add additional schemas that can be referenced by an URL using$ref. Use this to combine multiple schemas without changing the actual schema.

Each schema is referenced by their unique$id (since draft-06, previouslyid). Usually an$id is specified as an url, for examplehttps://mydomain.com/schema/schema-name or with a file extension likehttps://mydomain.com/schema/schema-name.json.

On a compiled schema

constschemaNode=compileSchema({$id:"https://sagold.com/local",type:"object",required:["character"],properties:{character:{$ref:"https://sagold.com/remote"}}});

use the exposed methodaddRemoteSchema to add a remote schema for $ref-resolution:

schemaNode.addRemoteSchema("https://sagold.com/remote",{$id:"https://sagold.com/remote",title:"A character",type:"string",minLength:1,maxLength:1});

Note the givenurl and$id on the root schema should match. If$id is omitted it will be added from the passed url.

To access the remote schema, add a $ref within your local schema and the remote schema will be resolved automatically:

schemaNode.validate({character:"AB"});// maxLength errorschemaNode.getData({});// { character: "A" } - default value resolved// returns remote schema (from compiled local schema):const{ node, error}=schemaNode.getNodeRef("https://sagold.com/remote");

Note JSON Schema $ref-keyword can become tricky when combined with $ids in sub-schemas. For more details, seejson-schema.org: Structuring a complex schema.

Adding remote schema to compileSchema

It is possible to pass remoteSchema on compileSchema by passing a SchemaNode (with all its remote schemas) inremote:

constremote=compileSchema({$id:"https://sagold.com/remote",$defs:{character:{title:"A character",type:"string",minLength:1,maxLength:1}}});constschemaNode=compileSchema({$ref:"https://sagold.com/remote#/defs/character"},{ remote});
Access local subschemas in remote schemas

You can add a local uri reference to the remote schema by using the# separator. The following example resolves hte local path/$defs/character in the remote schemahttps://sagold.com/remote throught the combined url:https://sagold.com/remote#/$defs/character

constschemaNode=compileSchema({$id:"https://sagold.com/local",$ref:"https://sagold.com/remote#/$defs/character"});schemaNode.addRemoteSchema("https://sagold.com/remote",{$defs:{character:{title:"A character",type:"string",minLength:1,maxLength:1}}});schemaNode.validate("AB");// maxLength errorschemaNode.getData("A");// "A" - default value resolved// returns remote schema (from compiled local schema):const{ node, error}=schemaNode.getNodeRef("https://sagold.com/remote#/$defs/character");

Note JSON Pointer are not restricted to$defs (definitions), but can reference any subschema. For example:

constschemaNode=compileSchema({$id:"https://sagold.com/local",$ref:"https://sagold.com/remote#/properties/character"});schemaNode.addRemoteSchema("https://sagold.com/remote",{type:"object",properties:{character:{title:"A character",type:"string",minLength:1,maxLength:1}}});schemaNode.validate("AB");// maxLength errorschemaNode.getData("A");// "A" - default value resolved// returns remote schema (from compiled local schema):schemaNode.getNodeRef("https://sagold.com/remote#/properties/character");

compileSchema

node.compileSchema creates a new schema node in the same context as node. With this, the created node will be able to resolve local$ref and remote$ref correctly. Note, the created schema will not be part of (linked) from any nodes in the schema-tree.

constsomeNode=node.compileSchema({prefixItems:[{type:"string"},{$ref:"#/$defs/string"}]});

createSchema

createSchema returns a simple JSON Schema for the input data.

constschemaNode=compileSchema(mySchema);constschema:JsonSchema=schemaNode.createSchema({title:"initial value"});console.log(schema);// { type: "string" }

getChildSelection

getChildSelection returns a list of available sub-schemas for the given property. In many cases, a single schema will be returned. ForoneOf-schemas, a list of possible options is returned. This helper always returns a list of schemas.

constschemaNode=compileSchema(mySchema);constschemas:SchemaNode[]=schemaNode.getChildSelection("content");
Example
import{compileSchema,JsonSchema}from"json-schema-library";constjsonSchema=compileSchema({type:"object",properties:{content:{oneOf:[{type:"string"},{type:"number"}]}}});constchildNodes:JsonSchema[]=jsonSchema.getChildSelection("content");expect(childNodes.map((n)=>n.schema)).to.deep.equal([{type:"string"},{type:"number"}]);

getData

getData creates input data from a JSON Schema that is valid to the schema. Where possible, the JSON Schemadefault property will be used to initially setup input data. Otherwise, the first values encountered (enum values, initial values, etc.) are used to build up the json-data.

constmyData=compileSchema(myJsonSchema).getData();

Additionally, you can pass input data.getData will then complement any missing values from the schema, while keeping the initial values.

constmyData=compileSchema(myJsonSchema).getData({name:"input-data"});

Note If you are using references in your schema,getData will only resolve the first$ref in each path, ensuring no infinite data structures are created. In case the limit of1$ref resolution is too low, you can modify the value globally one by adjusting the json-schema-library settings:

constmyData=compileSchema(myJsonSchema).getData(inputData,{recursionLimit:2});
Example
import{compileSchema,JsonSchema}from"json-schema-library";constmyJsonSchema:JsonSchema={type:"object",required:["name","option","list"],properties:{name:{type:"string"},option:{type:"string",enum:["first-option","second-option"]},list:{type:"array",items:{type:"string",default:"new item"},minItems:1}}};constschemaNode=newcompileSchema(myJsonSchema);constmyData=schemaNode.getData();expect(myData).to.deep.equal({name:"",option:"first-option",list:["new item"]});
Example with input data
import{compileSchema,JsonSchema}from"json-schema-library";constmyJsonSchema:JsonSchema={type:"object",required:["name","option","list"],properties:{name:{type:"string"},option:{type:"string",enum:["first-option","second-option"]},list:{type:"array",items:{type:"string",default:"new item"},minItems:1}}};constjsonSchema=compileSchema(myJsonSchema);constmyData=jsonSchema.getData({name:"input-data",list:[]});expect(myData).to.deep.equal({name:"input-data",option:"first-option",list:["new item"]});
Option: extendDefaults (default: false)

Per default,getData does try to create data that is valid to the json-schema. Example: array-schemas withminItems: 1 will add one item to fullfil the validation criteria. You can use the option and pass{ extendDefaults: false } to override this behaviour with a default value:

import{compileSchema}from"json-schema-library";constmyJsonSchema={type:"array",default:[],// if omitted will add an array itemitems:{type:"string",enum:["one","two"]},minItems:1// usually adds an enty, but default states: []};constmyData=compileSchema(myJsonSchema).getData(undefined,{extendDefaults:false});expect(myData).to.deep.equal([]);
Option: addOptionalProps (default: false)

getData will only add required properties per default:

constdata=compileSchema({required:["title"],properties:{title:{type:"string"},subTitle:{type:"string",default:"sub-title"}}}).getData(undefined);console.log(data);// { title: "" }

WithaddOptionalProps:true,getData will also add all optional properties

constdata=compileSchema({required:["title"],properties:{title:{type:"string"},subTitle:{type:"string",default:"sub-title"}}}).getData(undefined,{addOptionalProps:true});console.log(data);// { title: "", subTitle: "sub-title" }
Option: removeInvalidData (default: false)

WithremoveInvalidData:true,getData will remove data that is invalid to the given schema;

constdata=compileSchema({properties:{valid:{type:"string"}},additionalProperties:false}).getData({valid:"stays",invalid:"removed"},{removeInvalidData:true});console.log(data);// { valid: "stays" }

removeInvalidData:true willnot remove data that is valid, but unspecified:

constdata=compileSchema({properties:{valid:{type:"string"}},additionalProperties:true}).getData({valid:"stays",invalid:"removed"},{removeInvalidData:true});console.log(data);// { valid: "stays", invalid: "removed" }
Option: useTypeDefaults (default: true)

WithuseTypeDefaults:true,getData will return initial values for all primitives (non-objects/arrays) that do not have a default-property set:

constdata=compileSchema({required:["valid"],properties:{valid:{type:"string"}}}).getData(null,{useTypeDefaults:true});console.log(data);// { valid: "" }

SettinguseTypeDefaults:false willnot remove data that is valid, but unspecified:

constdata=compileSchema({required:["valid"],properties:{valid:{type:"string"}}}).getData(null,{useTypeDefaults:false});console.log(data);// {}

Note object and array-properties will still be added if required:

constdata=compileSchema({required:["valid"],properties:{valid:{type:"object"}}}).getData(null,{useTypeDefaults:true});console.log(data);// { valid: {}}

Note enforced array-items will beundefined if required andinitialValues: false

constdata=compileSchema({required:["valid"],properties:{valid:{type:"array",items:{type:"string"},minItems:1}}}).getData(null,{useTypeDefaults:false});console.log(data);// { valid: [undefined] }

getNode

getNode returns the JSON Schema from data location specified by a JSON Pointer. In many cases the JSON Schema can be retrieved without passing any data, but in situations where the schema is dynamic (for example inoneOf,dependencies, etc.), input-data is required orgetNode will return aJsonError as is done when the JSON Pointer path is invalid.

const{ node, error}=compileSchema(mySchema).getNode("/list/1/name",myData);if(node)console.log(node.schema);

NotegetNode will return anode=undefined for paths that lead to valid properties, but miss a schema definition. For example:

const{ node, error}=compileSchema({type:"object"}).getNode("/name");console.log(node,error);// undefined, undefined

In case this is unwanted behaviour, use thewithSchemaWarning option to return a json-error with codeschema-warning instead:

constschemaNode=compileSchema({type:"object"});const{ node, error}=schemaNode.getNode("/name",undefined,{withSchemaWarning:true});console.log(node?.schema,error);// undefined, { type: "error", code: "schema-warning" }

Or setgetNode to return a simple JSON Schema for the found data settingcreateSchema: true:

constschemaNode=compileSchema({type:"object"});const{ node, error}=schemaNode.getNode("/name",{name:123},{createSchema:true});console.log(node?.schema,error);// { type: "number" }, undefined
Example
import{compileSchema}from"json-schema-library";constmySchema={type:"object",properties:{list:{type:"array",items:{oneOf:[{type:"object",required:["name"],properties:{name:{type:"string",title:"name of item"}}},{type:"object",required:["description"],properties:{description:{type:"string",title:"description of item"}}}]}}}};const{ node}=compileSchema(mySchema).getNode("/list/1",{list:[{description:"..."},{name:"my-item"}]});expect(node.schema).to.deep.equal({type:"object",required:["name"],properties:{name:{type:"string",title:"name of item"}}});
Evaluating errors

All returned json-errors have a data property with the following properties

  • pointer JSON Pointer to the location where the error occured. In case of omitted data, this is the last JSON Schema location that could be resolved
  • schema the JSON Schema of the last resolved location and the source of the error
  • value the data value at this location that could not be resolved
const{ error}=schemaNode.getNode("/list/1");if(error){console.log(Object.keys(error.data));// [pointer, schema, value]}
About JsonPointer

JSON Pointer defines a string syntax for identifying a specific value within a Json document and issupported by Json-Schema. Given a Json document, it behaves similar to alodash path (a[0].b.c), which follows JS-syntax, but instead uses/ separators (e.g.,a/0/b/c). In the end, you describe a path into the Json data to a specific point.

getNodeChild

getNodeChild retrieves the SchemaNode of a child property or index. Usingget it is possible to incrementally go through the data, retrieving the schema for each next item.

constmySchema={type:"object",properties:{title:{type:"string"}}};constroot=compileSchema(mySchema);const{ node}=root.getNodeChild("title",{title:"value"});if(node==null)return;console.log(node.schema);
Example
import{compileSchema,JsonSchema}from"json-schema-library";constroot=compileSchema(mySchema);constlocalSchema:JsonSchema={oneOf:[{type:"object",properties:{title:{type:"string"}}},{type:"object",properties:{title:{type:"number"}}}]};constschema=root.getNodeChild("title",{title:4}).node?.schema;expect(schema).to.deep.eq({type:"number"});

getNodeRef

getNodeRef retrieves the SchemaNode of a$ref string.

constroot=compileSchema(mySchema);root.addRemoteSchema("https://remote.com/schema",remoteSchema);root.getNodeRef("#/$defs/title");// title-schema of mySchemaroot.getNodeRef("https://remote.com/schema");// remoteSchema

getNodeRoot

getNodeRoot returns the rootNode containing the initial JSON Schema

constroot=compileSchema(mySchema);const{ node}=root.getNode("/image/title");if(node){assert(node.getNodeRoot()===root);// success}

reduceNode

reduceNode compiles dynamic JSON schema keywords of a SchemaNode according to the given data.This utility helps walking down the schema-tree with a set of data and it helps getting a mostlycomplete json-schema for a specific data-value.

const{node:reducedNode}=compileSchema({properties:{trigger:{type:"boolean"}}dependentSchemas:{trigger:{required:["title"],properties:{title:{type:"string"}}}}}).reduceNode({trigger:true});expect(reducedNode.schema).to.deep.eq({required:["title"],properties:{trigger:{type:"boolean"},title:{type:"string"}}});

⚠️ Please be aware that certain schema-definitions are lost when resolving or merging sub-schemas.This mainly refers to validation-properties, but also some ambigiuous schema might get overriden.

toDataNodes

toDataNodes collects all data-items (object,array andvalue) and their SchemaNode and return them as a list ofDataNodes:

typeDataNode={pointer:string;value:unknown;node:SchemaNode};constschemaNode=compileSchema(mySchema);constnodes:DataNode[]=schemaNode.toDataNodes(myData);
Example
import{compileSchema,JsonSchema,JsonPointer}from"json-schema-library";constmySchema:JsonSchema={type:"array",items:[{type:"number"},{type:"string"}]};constschemaNode=compileSchema(mySchema);schemaNode.toDataNodes([5,"nine"]).map((dataNode)=>({schema:dataNode.node.schema,value:dataNode.value,pointer:dataNode.pointer}));expect(calls).to.deep.equal([{schema:mySchema,value:[5,"nine"],pointer:"#"},{schema:{type:"number"},value:5,pointer:"#/0"},{schema:{type:"string"},value:"nine",pointer:"#/1"}]);

toSchemaNodes

toSchemaNodes collects all sub-schema definitions, like inproperties["property"],anyOf[1],contains,$defs["name"], etc. and returns them as a list ofSchemaNodes:

constnodes:SchemaNode[]=compileSchema(mySchema).toSchemaNodes();
Example
import{compileSchema,JsonSchema,SchemaNode}from"json-schema-library";constmySchema:JsonSchema={type:"array",items:{oneOf:[{type:"number"},{$ref:"#/$defs/value"}]},$defs:{value:{type:"string"},object:{type:"object"}}};constnodes=compileSchema(mySchema).toSchemaNodes(myCallback).map((node)=>node.schema);expect(calls).to.deep.equal([mySchema,{oneOf:[{type:"number"},{$ref:"#/$defs/value"}]},{type:"number"},{$ref:"#/$defs/value"},{type:"string"},{type:"object"}]);

validate

validate is a completeJSON Schema validator for your input data. Callingvalidate will return a list of validation errors for the passed data.

const{ valid, errors}=compileSchema(myJsonSchema).validate(myData);// { valid: boolean, errors: JsonError[] }
About type JsonError

Injson-schema-library all errors are in the format of aJsonError:

typeJsonError={type:"error";code:string;message:string;data?:{[p:string]:any};};

In almost all cases, a JSON Pointer is given onerror.data.pointer, which points to the source within data where the error occured. For more details on how to work with errors, refer to sectioncustom errors.

Example
constmyJsonSchema:JsonSchema={type:"object",additionalProperties:false};const{ errors}=compileSchema(myJsonSchema).validate({name:"my-data"});expect(errors).to.deep.equal([{type:"error",code:"no-additional-properties-error",message:"Additional property `name` in `#` is not allowed",data:{property:"name",properties:[],pointer:"#"}}]);

You can also use async validators to validate data with json-schema. For this, another property asyncErrors is exposed on validate:

const{ errorsAsync}=compileSchema(myJsonSchema).validate(myData);if(errorsAsync.length>0){constadditionalErrors=(awaitPromise.all(errorsAsync)).filter((err)=>err!=null);}

Per defaultjson-schema-library does not contain async validators, soerrorsAsync is always empty. If you add async validators, a list ofPromise<JsonError|undefined> is return and you need to resolve and filter non-errors (undefined) yourself.

NoteisValid only refers to errors.errorsAsync has to be evaluated separately

Example Async Validation
import{JsonSchemaValidator,draft2020}from"json-schema-library";// return Promise<JsonError>constcustomValidator:JsonSchemaValidator=async({ node, pointer, data})=>{returnnode.createError("type-error",{schema:{},    pointer,value:data});};constdraftList=[extendDraft(jsonEditorDraft,{keywords:{custom:customValidator}})];const{ isValid, errorsAsync}=compileSchema({custom:true}).validate("data");console.log(isValid,errors.length);// true, 0consterrors=awaitPromise.all(errorsAsync);console.log(errors);/// [{ code: "type-error", value: "data", pointer: "#", ... }]

Draft Customization

Extending a Draft ·Keyword

json-schema-library uses the concept ofdrafts to support different versions of the JSON Schema specification — such as Draft 04, Draft 07, or 2020-12 — and to allow customization of schema behavior.

Eachdraft describes how a schema should be parsed, validated, and interpreted. Drafts can also be extended or modified to change or enhance behavior, such as:

  • Replacing or adding new keywords (oneOf,if/then, custom ones, etc.)
  • Defining or overriding format validators (format: "email", etc.)
  • Customizing or localizing error messages
  • Tweaking how schema nodes behave during parsing or resolution

Out of the box, the library exports all compliant JSON Schema drafts:

import{draft04,draft06,draft07,draft2019,draft2020}from"json-schema-library";

When you compile a schema, the library will automatically select the correct draft based on the$schema field — or fall back to the last draft in the list:

compileSchema(schema,{drafts:[draft04,draft07,draft2020]});

ADraft is an object that defines the core behavior and extensions for a schema. It includes:

typeDraft={$schemaRegEx:string;version:DraftVersion;keywords:Keyword[];errors:ErrorConfig;formats:typeofformats;methods:{};};

Here’s a breakdown of what each piece does:

$schemaRegEx

A regex string that identifies whether a draft should be used for a given schema, based on the$schema property. For example:

draft.$schemaRegEx==="draft[-/]2020-12";// matches "$schema": "https://json-schema.org/draft/2020-12/schema"// matches "$schema": "draft-2020-12"

When compiling, drafts are matched from left to right — the first one that matches is used. If no match is found, thelast draft in the list is used as a fallback. If you're only using one draft, the$schemaRegEx check is skipped.

version

Describes the draft version (e.g.,"2020-12"). This is mostly used for debugging and logging.

keywords

A list of keyword handlers for that draft, such asproperties,allOf,oneOf,$ref, and more. Each keyword defines how the library should parse and validate that keyword. You can override, extend, or remove any keyword.Learn more inKeyword.

errors

An object mapping error types to either template strings or error functions. These can be used to customize error messages globally or define more intelligent error generation logic.

formats

An object mapping format names (like"email","uuid","date-time") to custom validation functions. You can override or add formats depending on your needs.

methods

Draft-specific implementations for certain core behaviors inSchemaNode, such as how child schemas are selected or how schemas are converted to data nodes. These can be overridden in custom drafts if needed.

Available methods
createSchema:typeofcreateSchema;getChildSelection:typeofgetChildSelection;getData:typeofgetData;toDataNodes:typeoftoDataNodes;

Extending a Draft

You may want to extend a draft when the default JSON Schema behavior does not fit your needs. Whether you want to add new keywords, modify error messages, or define custom formats for your validation,extendDraft helps you adjust the draft version to meet your specific requirements.

Examples:

import{extendDraft,draft2020,oneOfFuzzyKeyword,createCustomError,render,ErrorData}from"json-schema-library";constmyDraft=extendDraft(draft2020,{// Match all $schema$schemaRegEx:"",// Register a custom "oneOf" keyword, replacing the existing onekeywords:[oneOfFuzzyKeyword],formats:{// Add a new "format": "test", which returns an error when the value is "test"test:({ data, node, pointer})=>{if(data==="test"){returnnode.createError("test-error",{schema:node.schema,pointer:pointer,value:data,customValue:"test"});}}},errors:{// Add a new custom error "test-error""test-error":"Test error for value {{value}} - {{customValue}}",// Overwrite the existing MaxLengthError message"max-length-error":"Too many characters",// Add a dynamic MinLengthError with custom logic"min-length-error":(data:ErrorData)=>{if(data.minLength===1){return{type:"error",code:"min-length-one-error",message:"Input is required",          data};}return{type:"error",code:"min-length-error",message:render("Value in `{{pointer}}` is `{{length}}`, but should be `{{minimum}}` at minimum",data),        data};}}});

Overwrite a format validator

The built-in format validators may not always align with your specific requirements. For instance, you might need to validate the output of an<input type="time" />, which produces values in formats likeHH:MM orHH:MM:SS. In such cases, you can customize or overwrite the format validators to suit your needs usingextendDraft

Example of overwriting a format validator
import{extendDraft,draft2020}from"json-schema-library";/** * A Regexp that extends http://tools.ietf.org/html/rfc3339#section-5.6 spec. * The specification requires seconds and timezones to be a valid date format. * * matchTimeSecondsAndTimeOptional matches: * - HH:MM:SSz * - HH:MM:SS(+/-)HH:MM * - HH:MM:SS * - HH:MMz * - HH:MM(+/-)HH:MM * - HH:MM */constmatchTimeSecondsAndTimeOptional=/^(?<time>(?:([0-1]\d|2[0-3]):[0-5]\d(:(?<second>[0-5]\d|60))?))(?:\.\d+)?(?<offset>(?:z|[+-]([0-1]\d|2[0-3])(?::?[0-5]\d)?)?)$/i;constcustomTimeFormatDraft=extendDraft(draft2020,{formats:{// This example extends the default time formatter which validates against RFC3339time:({ node, pointer, data})=>{const{ schema}=node;if(typeofdata!=="string"||data===""){returnundefined;}// Use the Custom Regex to validate the date and time.constmatches=data.match(matchTimeSecondsAndTimeOptional);if(!matches){returnnode.createError("format-date-time-error",{value:data, pointer, schema});}// leap secondif(matches.groups.second==="60"){// Omitted the code here for brevity.}returnundefined;}}});const{ errors, valid}=compileSchema({type:"string",format:"time",$schema:"https://json-schema.org/draft/2020-12/schema"},{drafts:[customTimeFormatDraft]}).validate("15:31:12");console.assert(valid,errors.at(0)?.message);

Keyword

Keywords hold the main logic for JSON Schema functionality. EachKeyword corresponds to a JSON Schema keyword likeproperties,prefixItems,oneOf, etc and offers implementations toparse,validate,resolve andreduce. Note that support for each implementation is optional, dependending on the feature requirements. The main properties of aKeyword:

  • aKeyword is only processed if the specifiedkeyword is available as property on the JSON Schema
  • an optionalorder property may be added as order of keyword execution is sometimes important (additionalItems last,$ref evaluation first)
  • the list of keywords is unique by property-valuekeyword
  • for a given functionaddX, a functionX must be present
typeKeyword={id:string;keyword:string;order?:number;parse?:(node:SchemaNode)=>void;addResolve?:(node:SchemaNode)=>boolean;resolve?:JsonSchemaResolver;addValidate?:(node:SchemaNode)=>boolean;validate?:JsonSchemaValidator;addReduce?:(node:SchemaNode)=>boolean;reduce?:JsonSchemaReducer;};

For examples on keyword implementations refer to./src/keywords.

parse

parse will be executed on compile time, usually to add a compiled sub-schema on the parent-node.

Example of keyword using parse
exportconstnotKeyword:Keyword={id:"not",keyword:"not",parse:parseNot};exportfunctionparseNot(node:SchemaNode){const{ schema, evaluationPath, schemaLocation}=node;if(schema.not!=null){node.not=node.compileSchema(schema.not,`${evaluationPath}/not`,`${schemaLocation}/not`);}}

resolve

A resolver returns a child-schema for a property-key, item-index or undefined if the key does not apply.

Example of keyword using resolve
exportconstpropertiesKeyword:Keyword={id:"property",keyword:"properties",parse:parseProperties,addResolve:(node:SchemaNode)=>node.properties!=null,resolve:propertyResolver};functionpropertyResolver({ node, key}:JsonSchemaResolverParams){returnnode.properties?.[key];}

reduce

A reducer replaces the JSON Schema keyword to a simple, static JSON Schema based on the current data

Example of keyword using reduce
exportconsttypeKeyword:Keyword={id:"type",keyword:"type",addReduce:(node)=>Array.isArray(node.schema.type),reduce:reduceType,addValidate:({ schema})=>schema.type!=null,validate:validateType};functionreduceType({ node, pointer, data}:JsonSchemaReducerParams):undefined|SchemaNode{constdataType=getJsonSchemaType(data,node.schema.type);if(dataType!=="undefined"&&Array.isArray(node.schema.type)&&node.schema.type.includes(dataType)){returnnode.compileSchema({ ...node.schema, pointer,type:dataType},node.evaluationPath);}returnundefined;}

Currentlykeywords are not exposed per default. You can still access any keyword implementation by retrieving them from a draft:

import{draft07}from"json-schema-library";constdependentSchemasKeyword=draft2020.keywords.find((f)=>f.keyword==="dependentSchemas");

Keyword extensions

oneOfProperty

ForoneOf resolution, JSON Schema states that data is valid if it validates against exactly one of those sub-schemas. In some scenarios this is unwanted behaviour, as the actualoneOf schema is known and only validation errors of this exact sub-schema should be returned.

For an explicitoneOf resolution, the JSON Schema may be extended by a propertyoneOfProperty. This will always associate an entry with a matching value (instead of schema validation) and return only this schema or validation errors, depending on the current task. For example:

constschema={oneOfProperty:"id",oneOf:[{type:"object",properties:{id:{const:"1"},title:{type:"number"}}},{type:"object",properties:{id:{const:"2"},title:{type:"number"}}},{type:"object",properties:{id:{const:"3"},title:{type:"number"}}}]};constresolvedNode=compileSchema(schema).reduce({id:"2",title:"not a number"});// will always return (even if invalid)expect(resolvedNode?.schema).to.deep.eq({type:"object",properties:{id:{const:"2"},title:{type:"number"}}});

oneOfFuzzyKeyword

If you're working with complex schemas that use theoneOf keyword to validate multiple options,oneOfFuzzyKeyword offers an alternative approach. It scores the schemas to return the best match, even if none of the schemas fully validate the input data. This makes error messages more readable and helps identify the most appropriate schema when multiple options exist.

oneOfFuzzyKeyword helps when no schema fully validates the data but you want to prioritize schemas based on how well they fit the input. This makes it easier to interpret validation results for complex conditions.

oneOfFuzzyKeyword is exposed byjson-schema-library and can be used to extend any draft.

import{extendDraft,oneOfFuzzyKeyword,draft2020}from"json-schema-library";constmyDraft=extendDraft(draft2020,{keywords:[oneOfFuzzyKeyword]});

errorMessages

You can set custom errors messages locally by using the errors-keyword:

const{ errors}=compileSchema({type:"array",minItems:2,errorMessages:{"min-items-error":"Custom error {{minItems}}"}}).validate([1]);assert.deepEqual(errors[0].message,"Custom error 2");

regexFlags

In order to allow customization of regular expressions a schema propertyregexFlags is supported forpattern,patternProperties and formatregex:

const{ errors}=compileSchema({type:"string",pattern:"^[a-zA-Z0-9+_.-]+",regexFlags:"v"}).validate("-");

Per default, a regexFlags"u" is used. To change this setting globally, changeREGEX_FLAGS in settings:

importsettingsfrom"json-schema-library";settings.REGEX_FLAGS="v";

Breaking Changes

v10.5.0

  • added support for ref resolution in getSchemaType

v10.4.0

  • introduced esm module export
  • fixed typo in argumentdisableRecusionLimit disableRecursionLimit
  • added settings to exports for global changes of settings

v10.3.0

  • introduce settingREGEX_FLAGS and schema-propertyregexFlags to customize regex flags to use evaluating regex
  • fixed an issue resolving non-URI compatible $ref-targets containing#/definitions

v10.2.0

  • introduce getData settinguseTypeDefaults
  • introduce support to merge meta-properties using $ref-resolution

v10.1.0

  • replacednode.additionalItems bynode.items for drafts below 2020-12
  • fixedadditionalItems behaviour to be ignored whenschema.items is not an array

v10.0.0

This update involves some significant changes in how you work with the library, so please carefully review the migration guide and adjust your implementation accordingly.

In version v10.0.0, we've made significant changes to the library’s API, particularly in how we handle drafts and schemas. These changes are required to support features likedynamicAnchor,unevaluatedItems, andoneOfIndex and to integrate with the headless-json-editor. The previous approach of directly working with JSON schema objects lacked the flexibility needed for more advanced features and extensibility.

The new implementation revolves around compiling schemas into aSchemaNode tree. This change offers a more fitting, simpler, and extensible approach to working with JSON schemas.

Key Changes:

  • Compile Schema: ThecompileSchema function now replaces the previous Draft-Class approach.
  • SchemaNode Representation: All schemas are now represented asSchemaNode, which holds the schema and provides an easier way to work with them.

Breaking Changes:

compileSchema is now a standalone function and replaces theDraft class. All return values for JSON Schema are nowSchemaNode objects that contain aschema property.

// PREVIOUSLYconstdraft=newDraft(schema);// NOWconstnode=compileSchema(schema);

Changed Methods:

  • draft.createSchemaOf(schema)node.createSchema(schema)
  • draft.each(data, callback)const nodes = node.toDataNodes(data)
  • draft.eachSchema(callback)const nodes = node.toSchemaNodes()
  • draft.getChildSchemaSelection(property)node.getChildSelection(property)
  • draft.getNode(options)node.getNode(pointer, data, options)
  • draft.getTemplate(inputData)node.getData(inputData)
  • draft.isValid(data)node.validate(data).valid
  • draft.step(property, data)node.getNodeChild(property, data)

Renamed Properties:templateDefaultOptionsgetDataDefaultOptions

Draft Customization: Customizing drafts has changed completely. The previous methods of extending drafts are no longer valid, and draft handling is now centered aroundSchemaNode.

Removed Error Propertyname: Error propertyname has been removed fromJsonError in favor ofcode.

Removed Configuration Option: ThetemplateDefaultOptions property has been removed from the global settings object. You should now configure it using thecompileSchema options:

compileSchema(schema,{getDataDefaultOptions:{addOptionalProps:false,removeInvalidData:false,extendDefaults:true}});

Changed remote $id support inaddRemoteSchema. An$id has to be a valid url (previously any value was accepted)

v9.0.0

breaking changes:

  • getSchema signature changed in favour of an options object. Instead ofdraft.getNode(pointer, data) arguments have to be passed as an objectdraft.getNode({ pointer, data }). This removes setting unwanted optional arguments and keeps the api more stable in the future (e.g.withSchemaWarning option)
  • JsonError now must exposepointer,schema andvalue consistently on data property

updates

  • getSchema consistently returns errors and can return errors for empty schema usingwithSchemaWarning option

v8.0.0

With versionv8.0.0,getData was improved to better support optional properties and utilize existing core logic, making it more reliable. Breaking changes:

  • RenamedJSONError toJsonError andJSONSchema toJsonSchema
  • getData only adds required properties. Behaviour can be changed bygetData default options
  • Internal schema propertyoneOfSchema has been replaced byschema.getOneOfOrigin()
  • Changedunique-items-error to point to error for duplicated item and changed data-properties
  • RemovedSchemaService as it was no longer used nor tested
Exposed new helper functions
  • mergeSchema - Merges to two json schema
  • reduceNode - Reduce schema by merging dynamic constructs into a static json schema omitting those properties
  • isDynamicSchema - Returns true if the passed schema contains dynamic properties (if,dependencies,allOf, etc)
  • resolveDynamicSchema - Resolves all dynamic schema definitions for the given input data and returns the resulting JSON Schema without any dynamic schema definitions.

About

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors13

Languages


[8]ページ先頭

©2009-2025 Movatter.jp