JSON Schema

Example Output

personinfo.schema.json

Overview

JSON Schema is a schema language for JSON documents.

JSON-Schema can be generated from a LinkML schema and used to validate JSON documents using standard JSON-Schema validators.

To run:

gen-json-schema personinfo.yaml > personinfo.schema.json

To use this in combination with the standard python jsonschema validator (bundled with linkml):

jsonschema -i data/example_personinfo_data.yaml personinfo.schema.json

See also

Data Validation for other validation strategies

Note

Note that any JSON that conforms to the derived JSON Schema can be converted to RDF using the derived JSON-LD context.

Inheritance

Because JSON-Schema does not support inheritance hierarchy, slots are “rolled down” from parent.

For example, in the personinfo schema, slots such as id and name are inherited from NamedThing, and aliases are inherited from a mixin:

NamedThing:
  slots:
    - id
    - name

HasAliases:
  mixin: true
  attributes:
    aliases:
      multivalued: true

Person:
  is_a: NamedThing
  mixins:
    - HasAliases
  slots:
    - birth_date
    - age_in_years
    - gender

(some parts truncated for brevity)

This would generate the following JSON-Schema:

"Person": {
   "additionalProperties": false,
   "description": "A person (alive, dead, undead, or fictional).",
   "properties": {
      "age_in_years": {
         "type": "integer"
      },
      "aliases": {
         "items": {
            "type": "string"
         },
         "type": "array"
      },
      "birth_date": {
         "type": "string"
      },
      "gender": {
         "$ref": "#/definitions/GenderType"
      },
      "id": {
         "type": "string"
      },
      "name": {
         "type": "string"
      },
   },
   "required": [
      "id"
   ],
   "title": "Person",
   "type": "object"
},

Composition

JSON-Schema supports schema composition through:

  • allOf

  • anyOf

  • oneOf

  • not

See Schema Composition

LinkML 1.2 supports analogous constructs:

Note that many uses of the above constructs may be better handled by using inheritance (see below) in LinkML. Future versions of LinkML may support translation of certain object oriented patterns to JSON-Schema compositions, for example:

  • The union_of slot could be used to generate oneOf

Inlining

LinkML separates the underlying logical model from choices of how references are inlined in JSON.

If an inlined directive is added to a slot definition as follows:

has_employment_history:
  range: EmploymentEvent
  multivalued: true
  inlined: true
  inlined_as_list: true

then the JSON-Schema will use a $ref:

"has_employment_history": {
   "items": {
      "$ref": "#/definitions/EmploymentEvent"
   },
   "type": "array"
},

However, if a slot is not inlined and the range is a class with an identifier, then the reference is by key.

For example, given:

FamilialRelationship:
  is_a: Relationship
  slot_usage:
    related to:
      range: Person
      required: true

Here the value of related_to is expected to be a string must be an identifier for a Person object:

the range is treated as a simple string in the JSON-Schema

"FamilialRelationship": {
   "additionalProperties": false,
   "description": "",
   "properties": {
      "ended_at_time": {
         "format": "date",
         "type": "string"
      },
      "related_to": {
         "type": "string"
      },
      "started_at_time": {
         "format": "date",
         "type": "string"
      }
   },
   "required": [
      "type",
      "related_to"
   ],
   "title": "FamilialRelationship",
   "type": "object"
},

Thus the JSON-Schema loses some information that is useful for validation, and for understanding of the schema.

LinkML also supports the ability to inline multivalued slots as dictionaries, where the key is the object identifier. See Inlining

This example schema supports inlining a list of people as a dictionary:

classes:
  Container:
    tree_root: true
    attributes:
      persons:
        range: Person
        inlined: true
        multivalued: true
  Person:
    attributes:
      name:
        identifier: true
      age:
        range: integer
        required: true
      gender:
        range: string
        required: true

The following data is conformant according to LinkML semantics:

{
 "persons":
   {
     "Bob": {
         "age": 42,
         "gender": "male"
     },
     "Alice": {
         "age": 37,
         "gender": "female"
     }
   }
}

This presents an additional complication when generating JSON-Schema: semantically the name field is required (all identifiers are automatically required in json-schema). However, we don’t want it to be required in the body of the dictionary since it is already present as a key.

The JSON-Schema generator takes care of this for you by making an alternative “laxer” version of the Person class that is used for validating the body of the persons dict.

This is what the underlying JSON-Schema looks like:

"$defs": {
   "Person": {
      "additionalProperties": false,
      "description": "",
      "properties": {
         "age": {
            "type": "integer"
         },
         "gender": {
            "type": "string"
         },
         "name": {
            "type": "string"
         }
      },
      "required": [
         "name",
         "age",
         "gender"
      ],
      "title": "Person",
      "type": "object"
   },
   "Person__identifier_optional": {
      "additionalProperties": false,
      "description": "",
      "properties": {
         "age": {
            "type": "integer"
         },
         "gender": {
            "type": "string"
         },
         "name": {
            "type": "string"
         }
      },
      "required": [
         "age",
         "gender"
      ],
      "title": "Person",
      "type": "object"
   }
},
"$id": "http://example.org",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
   "persons": {
      "additionalProperties": {
         "$ref": "#/$defs/Person__identifier_optional"
      }
   }
},
"title": "example.org",
"type": "object"

Patterns

Both LinkML and JSON-Schema support the same subset of ECMA-262 regular expressions.

See Regular Expressions.

For example, the following schema fragment

classes:
  # ...
  Person:
    # ...
    slot_usage:
      primary_email:

will generate:

"primary_email": {
   "pattern": "^\\S+@[\\S+\\.]+\\S+",
   "type": "string"
}

Enums

Enumerations are treated as simple strings. If the LinkML schema has additional metadata about the enumeration values, this is lost in translation.

Example:

classes:
  # ...
  FamilialRelationship:
    is_a: Relationship
    slot_usage:
      type:
        range: FamilialRelationshipType
        required: true
      related to:
        range: Person
        required: true
  #...

enums:
  FamilialRelationshipType:
    permissible_values:
      SIBLING_OF:
        description: a relationship between two individuals who share a parent
      PARENT_OF:
        description: a relationship between a parent (biological or non-biological) and their child
      CHILD_OF:
        description: inverse of the PARENT_OF type

Generates

"FamilialRelationship": {
   "additionalProperties": false,
   "description": "",
   "properties": {
      "ended_at_time": {
         "format": "date",
         "type": "string"
      },
      "related_to": {
         "type": "string"
      },
      "started_at_time": {
         "format": "date",
         "type": "string"
      },
      "type": {
         "$ref": "#/definitions/FamilialRelationshipType"
      }
   },
   "required": [
      "type",
      "related_to"
   ],
   "title": "FamilialRelationship",
   "type": "object"
},
"FamilialRelationshipType": {
   "description": "",
   "enum": [
      "SIBLING_OF",
      "PARENT_OF",
      "CHILD_OF"
   ],
   "title": "FamilialRelationshipType",
   "type": "string"
},

Docs

Command Line

gen-json-schema

Generate JSON Schema representation of a LinkML model

gen-json-schema [OPTIONS] YAMLFILE

Options

-i, --inline

Generate references to types rather than inlining them. Note that declaring a slot as inlined: true will always inline the class

-t, --top-class <top_class>

Top level class; slots of this class will become top level properties in the json-schema

--not-closed, --closed

Set additionalProperties=False if closed otherwise true if not closed at the global level

Default

True

--include-range-class-descendants, --no-range-class-descendants

When handling range constraints, include all descendants of the range class instead of just the range class

-f, --format <format>

Output format

Default

json

Options

json

--metadata, --no-metadata

Include metadata in output

Default

True

--useuris, --metauris

Include metadata in output

Default

True

-im, --importmap <importmap>

Import mapping file

--log_level <log_level>

Logging level

Default

WARNING

Options

CRITICAL | ERROR | WARNING | INFO | DEBUG

-v, --verbose

verbosity

--mergeimports, --no-mergeimports

Merge imports into source file (default=mergeimports)

Arguments

YAMLFILE

Required argument

Code

class linkml.generators.jsonschemagen.JsonSchemaGenerator(schema: ~typing.Union[str, ~typing.TextIO, ~linkml_runtime.linkml_model.meta.SchemaDefinition, ~linkml.utils.generator.Generator], schemaview: ~typing.Optional[~linkml_runtime.utils.schemaview.SchemaView] = None, format: ~typing.Optional[str] = None, metadata: bool = <factory>, useuris: ~typing.Optional[bool] = None, log_level: int = 30, mergeimports: ~typing.Optional[bool] = <factory>, source_file_date: ~typing.Optional[str] = None, source_file_size: ~typing.Optional[int] = None, logger: ~typing.Optional[~logging.Logger] = None, verbose: ~typing.Optional[bool] = None, output: ~typing.Optional[str] = None, namespaces: ~typing.Optional[~linkml_runtime.utils.namespaces.Namespaces] = None, directory_output: bool = False, base_dir: ~typing.Optional[str] = None, metamodel_name_map: ~typing.Optional[~typing.Dict[str, str]] = None, importmap: ~typing.Optional[~typing.Union[str, ~typing.Mapping[str, str]]] = None, emit_prefixes: ~typing.Set[str] = <factory>, metamodel: ~typing.Optional[~linkml.utils.schemaloader.SchemaLoader] = None, topClass: ~typing.Optional[str] = None, not_closed: ~typing.Optional[bool] = <factory>, schemaobj: ~typing.Optional[~jsonasobj2._jsonobj.JsonObj] = None, clsobj: ~typing.Optional[~jsonasobj2._jsonobj.JsonObj] = None, inline: bool = False, top_class: ~typing.Optional[~linkml_runtime.linkml_model.meta.ClassDefinitionName] = None, entryProperties: dict = <factory>, include_range_class_descendants: bool = <factory>, optional_identifier_class_map: ~typing.Dict[str, ~typing.Tuple[str, str]] = <factory>, **_kwargs)[source]

Generates JSONSchema documents from a LinkML SchemaDefinition

  • Each linkml class generates a schema

  • inheritance hierarchies are rolled-down from ancestors

  • Composition not yet implemented

  • Enumerations treated as strings

  • Foreign key references are treated as semantics-free strings

serialize(**kwargs) str

Generate output in the required format

Parameters

kwargs – Generater specific parameters

Returns

Generated output