Metadata#
The tree-sequence and all the entities within it (nodes, mutations, edges, etc.) can have metadata associated with them. This is intended for storing and passing on information that tskit itself does not use or interpret, for example information derived from a VCF INFO field, or administrative information (such as unique identifiers) relating to samples and populations. Note that provenance information about how a tree sequence was created should not be stored in metadata, instead the provenance mechanisms in tskit should be used (see Provenance).
The metadata for each entity (e.g. row in a table) is described by a schema for each
entity type (e.g. table). The schemas allow the tskit Python API to encode and decode
metadata automatically and, most importantly, tells downstream users and tools how to
decode and interpret the metadata. For example, the msprime
schema for populations
requires both a name
and a description
for each defined population: these names and
descriptions can assist downstream users in understanding and using msprime
tree
sequences. It is best practice to populate such metadata fields if your files will be
used by any third party, or if you wish to remember what the rows refer to some time
after making the file!
Technically, schemas describe what information is stored in each metadata record, and
how it is to be encoded, plus some optional rules about the types and ranges of data
that can be stored. Every node’s metadata follows the node schema, every mutation’s
metadata the mutation schema, and so on. Most users of tree-sequence files will not
need to modify the schemas: typically, as in the example of msprime
above, schemas are
defined by the software which created the tree-sequence file. The exact metadata stored
depends on the use case; it is also possible for subsequent processes to add or modify
the schemas, if they wish to add to or modify the types (or encoding) of the metadata.
The metadata schemas are in the form of a JSON Schema (a good guide to JSON Schema is at Understanding JSON Schema). The schema must specify an object with properties, the keys and types of those properties are specified along with optional long-form names, descriptions and validations such as min/max or regex matching for strings, see the Schema examples below.
As a convenience the simplest, permissive JSON schema is available as
MetadataSchema.permissive_json()
.
The Working with Metadata Tutorial shows how to use schemas and access metadata in the tskit Python API.
Note that the C API simply provides byte-array binary access to the metadata, leaving the encoding and decoding to the user. The same can be achieved with the Python API, see Binary metadata.
Examples#
In this section we give some examples of how to define metadata schemas and how to add metadata to various parts of a tree sequence using the Python API. For simplicity, these initial examples use the JSON codec (see Codecs).
Top level#
Top level metadata is associated with the tree sequence as a whole, rather than any specific table. This is used, for example, by programs such as SLiM to store information about the sort of model that was used to generate the tree sequence (but note that detailed information used to recreate the tree sequence is better stored in Provenance).
Here’s an example of adding your own top-level metadata to a tree sequence:
import tskit
# Define some top-level metadata you might want to add to a tree sequence
top_level_metadata = {
"taxonomy": {"species": "Arabidopsis lyrata", "subspecies": "petraea"},
"generation_time": 2,
}
# Generate a simple tree sequence of one random tree.
ts = tskit.Tree.generate_random_binary(8, branch_length=10, random_seed=9).tree_sequence
# To edit a tree sequence, first dump it to tables.
tables = ts.dump_tables()
# Set the metadata schema for the top-level metadata
tables.metadata_schema = tskit.MetadataSchema.permissive_json() # simplest schema
# Set the metadata itself
tables.metadata = top_level_metadata
ts = tables.tree_sequence()
print(
"The tree sequence is of",
ts.metadata["taxonomy"]["species"],
"subsp.",
ts.metadata["taxonomy"]["subspecies"],
)
The tree sequence is of Arabidopsis lyrata subsp. petraea
In this case, the species and subspecies name are self-explanatory, but
the interpretation of the generation_time
field is less clear. Setting
a more precise schema will help other users of your tree sequence:
schema = {
"codec": "json",
"type": "object",
"properties": {
"generation_time": {"type": "number", "description": "Generation time in years"},
},
"additionalProperties": True, # optional: True by default anyway
}
tables.metadata_schema = tskit.MetadataSchema(schema)
tables.metadata = top_level_metadata # put the metadata back in
ts = tables.tree_sequence()
print(ts.metadata)
{'generation_time': 2, 'taxonomy': {'species': 'Arabidopsis lyrata', 'subspecies': 'petraea'}}
Note that the schema here only describes the generation_time
field. The
metadata also contains additional fields (such as the species) that are
not in the schema; this is allowed because additionalProperties
is True
(assumed by default in the json codec, but shown
above for clarity).
Explicitly specified fields are validated on input, helping to avoid errors. For example, setting the generation time to a string will now raise an error:
tables.metadata = {"generation_time": "two of your earth years"}
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
File ~/work/tskit-site/tskit-site/python/tskit/metadata.py:700, in MetadataSchema.validate_and_encode_row(self, row)
699 try:
--> 700 self._validate_row(row)
701 except jsonschema.exceptions.ValidationError as ve:
File /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/jsonschema/validators.py:451, in create.<locals>.Validator.validate(self, *args, **kwargs)
450 for error in self.iter_errors(*args, **kwargs):
--> 451 raise error
ValidationError: 'two of your earth years' is not of type 'number'
Failed validating 'type' in schema['properties']['generation_time']:
OrderedDict([('description', 'Generation time in years'),
('type', 'number')])
On instance['generation_time']:
'two of your earth years'
The above exception was the direct cause of the following exception:
MetadataValidationError Traceback (most recent call last)
Cell In[3], line 1
----> 1 tables.metadata = {"generation_time": "two of your earth years"}
File ~/work/tskit-site/tskit-site/python/tskit/metadata.py:853, in MetadataProvider.metadata(self, metadata)
851 @metadata.setter
852 def metadata(self, metadata: bytes | dict | None) -> None:
--> 853 encoded = self.metadata_schema.validate_and_encode_row(metadata)
854 self._ll_object.metadata = encoded
File ~/work/tskit-site/tskit-site/python/tskit/metadata.py:702, in MetadataSchema.validate_and_encode_row(self, row)
700 self._validate_row(row)
701 except jsonschema.exceptions.ValidationError as ve:
--> 702 raise exceptions.MetadataValidationError(str(ve)) from ve
703 return self.encode_row(row)
MetadataValidationError: 'two of your earth years' is not of type 'number'
Failed validating 'type' in schema['properties']['generation_time']:
OrderedDict([('description', 'Generation time in years'),
('type', 'number')])
On instance['generation_time']:
'two of your earth years'
Note
Although we have stored the generation time in metadata, the
time units of a tree sequence should be stored in the
time_units
attribute, not in
metadata. For example, we could set tables.time_units = "generations"
.
Reference sequence#
Often a genome will be associated with a
reference sequence for that species. In this case, we might want to
store not just the species name, but also e.g. the build version of
the reference sequence, and possibly the reference sequence itself.
There is built-in support for this in tskit, via the
metadata
and
metadata_schema
properties
of the TreeSequence.reference_sequence
attribute
(see the Reference sequence documentation).
Todo
Add examples of reference sequence metadata when the API becomes less preliminary. This should include an example where we declare (or better, use on we define in the library) a standard metadata schema for a species, which defines and documents accession numbers, genome builds, etc. e.g.
tables.reference_sequence.metadata_schema = standard_schema
tables.reference_sequence.metadata = {...}
ts = tables.tree_sequence()
Tables#
Each table in a tree sequence (apart from the provenance table) can have its own metadata, and associated metadata schema.
tables.individuals.metadata_schema = tskit.MetadataSchema.permissive_json()
tables.individuals.add_row(metadata={"Accession ID": "ABC123"})
ts = tables.tree_sequence()
print(",\n ".join(str(ts.individual(0)).split(", ")))
Individual(id=0,
flags=0,
location=array([],
dtype=float64),
parents=array([],
dtype=int32),
nodes=array([],
dtype=int32),
metadata={'Accession ID': 'ABC123'})
However, we might want something more descriptive than the default
permissive_json()
. schema. We could create
a new schema, or modify the existing one. Modification is useful
if a nontrivial schema has been set already, for example in the
case of populations
when the tree sequence has been generated by
msprime.sim_ancestry()
.
# Modify an existing schema
schema_as_python_dict = tables.individuals.metadata_schema.schema
if "properties" not in schema_as_python_dict:
schema_as_python_dict["properties"] = {}
schema_as_python_dict["properties"]["Accession ID"] = {
"type": "string", "description": "An accession ID for this individual"}
# Optional: require an accession id to be specified for all individuals
if "required" not in schema_as_python_dict:
schema_as_python_dict["required"] = []
schema_as_python_dict["required"].append("Accession ID")
# Set the schema back on the table
tables.individuals.metadata_schema = tskit.MetadataSchema(schema_as_python_dict)
# Put all the metadata back in, using validate_and_encode_row to validate it
tables.individuals.packset_metadata([
tables.individuals.metadata_schema.validate_and_encode_row(ind.metadata)
for ind in tables.individuals
])
print("New schema:", tables.individuals.metadata_schema)
New schema: tskit.MetadataSchema(
{'codec': 'json',
'properties': OrderedDict([('Accession ID',
OrderedDict([('description',
'An accession ID for this '
'individual'),
('type', 'string')]))]),
'required': ['Accession ID']}
)
Defaults#
Since we specified that the accession_id
property was required in the
example above, the user always has to provide it, otherwise it will
fail to validate:
tables.individuals.add_row(metadata={"Comment": "This has no accession ID"})
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
File ~/work/tskit-site/tskit-site/python/tskit/metadata.py:700, in MetadataSchema.validate_and_encode_row(self, row)
699 try:
--> 700 self._validate_row(row)
701 except jsonschema.exceptions.ValidationError as ve:
File /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/jsonschema/validators.py:451, in create.<locals>.Validator.validate(self, *args, **kwargs)
450 for error in self.iter_errors(*args, **kwargs):
--> 451 raise error
ValidationError: 'Accession ID' is a required property
Failed validating 'required' in schema:
OrderedDict([('codec', 'json'),
('properties',
OrderedDict([('Accession ID',
OrderedDict([('description',
'An accession ID for this '
'individual'),
('type', 'string')]))])),
('required', ['Accession ID'])])
On instance:
{'Comment': 'This has no accession ID'}
The above exception was the direct cause of the following exception:
MetadataValidationError Traceback (most recent call last)
Cell In[6], line 1
----> 1 tables.individuals.add_row(metadata={"Comment": "This has no accession ID"})
File ~/work/tskit-site/tskit-site/python/tskit/tables.py:904, in IndividualTable.add_row(self, flags, location, parents, metadata)
902 if metadata is None:
903 metadata = self.metadata_schema.empty_value
--> 904 metadata = self.metadata_schema.validate_and_encode_row(metadata)
905 return self.ll_table.add_row(
906 flags=flags, location=location, parents=parents, metadata=metadata
907 )
File ~/work/tskit-site/tskit-site/python/tskit/metadata.py:702, in MetadataSchema.validate_and_encode_row(self, row)
700 self._validate_row(row)
701 except jsonschema.exceptions.ValidationError as ve:
--> 702 raise exceptions.MetadataValidationError(str(ve)) from ve
703 return self.encode_row(row)
MetadataValidationError: 'Accession ID' is a required property
Failed validating 'required' in schema:
OrderedDict([('codec', 'json'),
('properties',
OrderedDict([('Accession ID',
OrderedDict([('description',
'An accession ID for this '
'individual'),
('type', 'string')]))])),
('required', ['Accession ID'])])
On instance:
{'Comment': 'This has no accession ID'}
However, rather than require a user-specified value, we can provide a
default, which will be returned if the field is absent. In this case the property
should not be marked as required
.
new_schema = {
"codec": "json",
"type": "object",
"properties": {
"Accession ID": {
"type": "string",
"description": "An accession ID for this individual",
"default": "N/A", # Default if this property is absent
},
},
"default": {"Accession ID": "N/A"}, # Default if no metadata in this row
}
tables.individuals.metadata_schema = tskit.MetadataSchema(new_schema)
tables.individuals.packset_metadata([
tables.individuals.metadata_schema.validate_and_encode_row(ind.metadata)
for ind in tables.individuals
])
tables.individuals.add_row(metadata={"Comment": "This has no accession ID"})
ts = tables.tree_sequence()
print("Newly added individual:")
print(",\n ".join(str(ts.individual(-1)).split(", ")))
Newly added individual:
Individual(id=1,
flags=0,
location=array([],
dtype=float64),
parents=array([],
dtype=int32),
nodes=array([],
dtype=int32),
metadata={'Accession ID': 'N/A',
'Comment': 'This has no accession ID'})
Note
In the json codec, defaults can only be set for the shallowest level of the metadata object.
Codecs#
The underlying metadata is in raw binary (see
data model) and so it
must be encoded and decoded. The C API does not do this, but the Python API will
use the schema to decode the metadata to Python objects.
The encoding for doing this is specified in the top-level schema property codec
.
Currently the Python API supports the json
codec which encodes metadata as
JSON, and the struct
codec which encodes
metadata in an efficient schema-defined binary format using struct.pack()
.
json
#
When json
is specified as the codec
in the schema the metadata is encoded in
the human readable JSON format. As this format
is human readable and encodes numbers as text it uses more bytes than the struct
format. However it is simpler to configure as it doesn’t require any format specifier
for each type in the schema. Tskit deviates from standard JSON in that
empty metadata is interpreted as an empty object. This is to allow setting of a schema
to a table with out the need to modify all existing empty rows.
struct
#
When struct
is specifed as the codec
in the schema the metadata is encoded
using struct.pack()
which results in a compact binary representation which
is much smaller and generally faster to encode/decode than JSON.
This codec places extra restrictions on the schema:
Each property must have a
binaryFormat
This sets the binary encoding used for the property.All metadata objects must have fixed properties. This means that additional properties not listed in the schema are disallowed. Any property that does not have a
default
specified in the schema must be present. Default values will be encoded.Arrays must be lists of homogeneous objects. For example, this is not valid:
{"type": "array", "items": [{"type": "number"}, {"type": "string"}]}
Types must be singular and not unions. For example, this is not valid:
{"type": ["number", "string"]}
One exception is that the top-level can be a union of
object
andnull
to support the case where some rows do not have metadata.The order that properties are encoded is by default alphabetically by name. The order can be overridden by setting an optional numerical
index
on each property. This is due to objects being unordered in JSON and Pythondicts
.
binaryFormat#
To determine the binary encoding of each property in the metadata the binaryFormat
key is used.
This describes the encoding for each property using struct
format characters.
For example an unsigned 8-byte integer can be specified with::
{"type": "number", "binaryFormat":"Q"}
And a length 10 string with::
{"type": "string", "binaryFormat":"10p"}
Some of the text below is copied from the python docs.
Numeric and boolean types#
The supported numeric and boolean types are:
Format |
C Type |
Python type |
Numpy type |
Size in bytes |
---|---|---|---|---|
|
_Bool |
bool |
bool |
1 |
|
signed char |
integer |
int8 |
1 |
|
unsigned char |
integer |
uint8 |
1 |
|
short |
integer |
int16 |
2 |
|
unsigned short |
integer |
uint16 |
2 |
|
int |
integer |
int32 |
4 |
|
unsigned int |
integer |
uint32 |
4 |
|
|
integer |
int32 |
4 |
|
unsigned long |
integer |
uint32 |
4 |
|
|
integer |
int64 |
8 |
|
unsigned long long |
integer |
uint64 |
8 |
|
float |
float |
float32 |
4 |
|
double |
float |
float64 |
8 |
When attempting to pack a non-integer using any of the integer conversion
codes, if the non-integer has a __index__
method then that method is
called to convert the argument to an integer before packing.
For the 'f'
and 'd'
conversion codes, the packed
representation uses the IEEE 754 binary32 or binary64 format (for
'f'
or 'd'
respectively), regardless of the floating-point
format used by the platform.
Note that endian-ness cannot be specified and is fixed at little endian.
When encoding a value using one of the integer formats ('b'
,
'B'
, 'h'
, 'H'
, 'i'
, 'I'
, 'l'
, 'L'
,
'q'
, 'Q'
), if the value is outside the valid range for that format
then struct.error
is raised.
For the '?'
format character, the decoded value will be either True
or
False
. When encoding, the truth value of the input is used.
Strings#
Format |
C Type |
Python type |
Size in bytes |
---|---|---|---|
|
pad byte |
no value |
as specified |
|
char |
string of length 1 |
1 |
|
char[] |
string |
as specified |
|
char[] |
string |
as specified |
For the 's'
format character, the number prefixed is interpreted as the length in
bytes, for example,
'10s'
means a single 10-byte string. For packing, the string is
truncated or padded with null bytes as appropriate to make it fit. For
unpacking, the resulting bytes object always has exactly the specified number
of bytes, unless nullTerminated
is true
, in which case it ends at the first
null
. As a special case, '0s'
means a single, empty string.
The 'p'
format character encodes a “Pascal string”, meaning a short
variable-length string stored in a fixed number of bytes, given by the count.
The first byte stored is the length of the string, or 255, whichever is
smaller. The bytes of the string follow. If the string to encode is too long
(longer than the count minus 1), only the leading
count-1
bytes of the string are stored. If the string is shorter than
count-1
, it is padded with null bytes so that exactly count bytes in all
are used. Note that strings specified with this format cannot be longer than 255.
Strings that are longer than the specified length will be silently truncated, note that the length is in bytes, not characters.
The string encoding can be set with stringEncoding
which defaults to utf-8
.
A list of possible encodings is
here.
For most cases, where there are no null
characters in the metadata
{"type":"string", "binaryFormat": "1024s", "nullTerminated": True}
is a good option
with the size set to that appropriate for the strings to be encoded.
Padding bytes#
Unused padding bytes (for compatibility) can be added with a schema entry like:
{"type": "null", "binaryFormat":"5x"} # 5 padding bytes
Arrays#
The codec stores the length of the array before the array data. The format used for the
length of the array can be chosen with arrayLengthFormat
which must be one
of B
, H
, I
, L
or Q
which have the same meaning as in the numeric
types above. L
is the default. As an example:
{"type": "array", {"items": {"type":"number", "binaryFormat":"h"}}, "arrayLengthFormat":"B"}
Will result in an array of 2 byte integers, prepended by a single-byte array-length.
For dealing with legacy encodings that do not store the
length of the array, setting noLengthEncodingExhaustBuffer
to true
will read
elements of the array until the metadata buffer is exhausted. As such an array
with this option must be the last type in the encoded struct.
Union typed metadata#
As a special case under the struct
codec, the top-level type of metadata can be a
union of object
and null
. Set "type": ["object", "null"]
. Properties should
be defined as normal, and will be ignored if the metadata is None
.
Schema examples#
Struct codec#
As an example here is a schema using the struct
codec which could apply, for example,
to the individuals in a tree sequence:
complex_struct_schema = {
"codec": "struct",
"type": "object",
"properties": {
"accession_number": {"type": "integer", "binaryFormat": "i"},
"collection_date": {
"description": "Date of sample collection in ISO format",
"type": "string",
"binaryFormat": "10p",
"pattern": "^([1-9][0-9]{3})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])?$",
},
"phenotype": {
"description": "Phenotypic measurements on this individual",
"type": "object",
"properties": {
"height": {
"description": "Height in metres, or NaN if unknown",
"type": "number",
"binaryFormat": "f",
"default": float("NaN"),
},
"age": {
"description": "Age in years at time of sampling, or -1 if unknown",
"type": "number",
"binaryFormat": "h",
"default": -1,
},
},
"default": {},
},
},
"required": ["accession_number", "collection_date"],
"additionalProperties": False,
}
# Demonstrate use
tables.individuals.clear()
tables.individuals.metadata_schema = tskit.MetadataSchema(complex_struct_schema)
tables.individuals.add_row(
metadata={"accession_number": 123, "collection_date": "2011-02-11"}
)
ts = tables.tree_sequence()
print(ts.individual(0).metadata)
This schema states that the metadata for each row of the table is an object consisting
of three properties. Property accession_number
is a number (stored as a 4-byte int).
Property collection_date
is a string which must satisfy a regex, which checks it is
a valid ISO8601 date. Property
phenotype
is itself an object consisting of the properties height
(a single precision
floating point number) and age (a 2 byte signed integer).
Because this is a struct codec, and neither of the the first two properties have a
default set, they must be marked as “required” (in the JSON codec if no default is given,
unspecified properties will simply be missing in the returned metadata dictionary).
Also because this is a struct codec, additionalProperties
must be set to False. This
is assumed by default in the struct codec, but has been shown above for clarity.
Python Metadata API Overview#
Schemas are represented in the Python API by the tskit.MetadataSchema
class which can be assigned to, and retrieved from, tables via their metadata_schema
attribute (e.g. tskit.IndividualTable.metadata_schema
). The schemas
for all tables can be retrieved from a tskit.TreeSequence
by the
tskit.TreeSequence.table_metadata_schemas
attribute.
The top-level tree sequence metadata schema is set via
tskit.TableCollection.metadata_schema
and can be accessed via
tskit.TreeSequence.metadata_schema
.
Each table’s add_row
method (e.g. tskit.IndividualTable.add_row()
) will
validate and encode the metadata using the schema. This encoding will also happen when
tree sequence metadata is set (e.g. table_collection.metadata = {...}
.
Metadata will be lazily decoded if accessed via
tables.individuals[0].metadata
. tree_sequence.individual(0).metadata
or
tree_sequence.metadata
In the interests of efficiency the bulk methods of set_columns
(e.g. tskit.IndividualTable.set_columns()
)
and append_columns
(e.g. tskit.IndividualTable.append_columns()
) do not
validate or encode metadata. See Metadata for bulk table methods for how to prepare
metadata for these methods.
Metadata processing can be disabled and raw bytes stored/retrieved. See Binary metadata.
Full metaschema#
The schema for metadata schemas is formally defined using
JSON Schema and given in full here. Any schema passed to
tskit.MetadataSchema
is validated against this metaschema.
{
"$id": "http://json-schema.org/draft-07/schema#",
"$schema": "http://json-schema.org/draft-07/schema#",
"codec": {"type": "string"},
"default": true,
"definitions": {
"nonNegativeInteger": {"minimum": 0, "type": "integer"},
"nonNegativeIntegerDefault0": {
"allOf": [{"$ref": "#/definitions/nonNegativeInteger"}, {"default": 0}]
},
"root": {
"$id": "http://json-schema.org/draft-07/schema#",
"$schema": "http://json-schema.org/draft-07/schema#",
"default": true,
"definitions": {
"nonNegativeInteger": {"minimum": 0, "type": "integer"},
"nonNegativeIntegerDefault0": {
"allOf": [
{"$ref": "#/definitions/nonNegativeInteger"},
{"default": 0},
]
},
"schemaArray": {
"items": {"$ref": "#/definitions/root"},
"minItems": 1,
"type": "array",
},
"simpleTypes": {
"enum": ["array", "boolean", "integer", "null", "number", "object", "string",]
},
"stringArray": {
"default": [],
"items": {"type": "string"},
"type": "array",
"uniqueItems": true,
},
},
"properties": {
"$comment": {"type": "string"},
"$id": {"format": "uri-reference", "type": "string"},
"$ref": {"format": "uri-reference", "type": "string"},
"$schema": {"format": "uri", "type": "string"},
"additionalItems": {"$ref": "#/definitions/root"},
"additionalProperties": {"$ref": "#/definitions/root"},
"allOf": {"$ref": "#/definitions/schemaArray"},
"anyOf": {"$ref": "#/definitions/schemaArray"},
"const": true,
"contains": {"$ref": "#/definitions/root"},
"contentEncoding": {"type": "string"},
"contentMediaType": {"type": "string"},
"default": true,
"definitions": {
"additionalProperties": {"$ref": "#/definitions/root"},
"default": {},
"type": "object",
},
"dependencies": {
"additionalProperties": {
"anyOf": [{"$ref": "#/definitions/root"},
{"$ref": "#/definitions/stringArray"},
]
},
"type": "object",
},
"description": {"type": "string"},
"else": {"$ref": "#/definitions/root"},
"enum": {"items": true, "type": "array"},
"examples": {"items": true, "type": "array"},
"exclusiveMaximum": {"type": "number"},
"exclusiveMinimum": {"type": "number"},
"format": {"type": "string"},
"if": {"$ref": "#/definitions/root"},
"items": {
"anyOf": [
{"$ref": "#/definitions/root"},
{"$ref": "#/definitions/schemaArray"},
],
"default": true,
},
"maxItems": {"$ref": "#/definitions/nonNegativeInteger"},
"maxLength": {"$ref": "#/definitions/nonNegativeInteger"},
"maxProperties": {"$ref": "#/definitions/nonNegativeInteger"},
"maximum": {"type": "number"},
"minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"},
"minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"},
"minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"},
"minimum": {"type": "number"},
"multipleOf": {"exclusiveMinimum": 0, "type": "number"},
"not": {"$ref": "#/definitions/root"},
"oneOf": {"$ref": "#/definitions/schemaArray"},
"pattern": {"format": "regex", "type": "string"},
"patternProperties": {
"additionalProperties": {"$ref": "#/definitions/root"},
"default": {},
"propertyNames": {"format": "regex"},
"type": "object",
},
"properties": {
"additionalProperties": {"$ref": "#/definitions/root"},
"default": {},
"type": "object",
},
"propertyNames": {"$ref": "#/definitions/root"},
"readOnly": {"default": false, "type": "boolean"},
"required": {"$ref": "#/definitions/stringArray"},
"then": {"$ref": "#/definitions/root"},
"title": {"type": "string"},
"type": {"enum": ["object"]},
"uniqueItems": {"default": false, "type": "boolean"},
},
"title": "Core schema meta-schema",
"type": ["object", "boolean"],
},
"schemaArray": {
"items": {"$ref": "#/definitions/root"},
"minItems": 1,
"type": "array",
},
"simpleTypes": {
"enum": ["array", "boolean", "integer", "null", "number", "object", "string",]
},
"stringArray": {
"default": [],
"items": {"type": "string"},
"type": "array",
"uniqueItems": true,
},
},
"properties": {
"$comment": {"type": "string"},
"$id": {"format": "uri-reference", "type": "string"},
"$ref": {"format": "uri-reference", "type": "string"},
"$schema": {"format": "uri", "type": "string"},
"additionalItems": {"$ref": "#/definitions/root"},
"additionalProperties": {"$ref": "#/definitions/root"},
"allOf": {"$ref": "#/definitions/schemaArray"},
"anyOf": {"$ref": "#/definitions/schemaArray"},
"const": true,
"contains": {"$ref": "#/definitions/root"},
"contentEncoding": {"type": "string"},
"contentMediaType": {"type": "string"},
"default": true,
"definitions": {
"additionalProperties": {"$ref": "#/definitions/root"},
"default": {},
"type": "object",
},
"dependencies": {
"additionalProperties": {
"anyOf": [
{"$ref": "#/definitions/root"},
{"$ref": "#/definitions/stringArray"},
]
},
"type": "object",
},
"description": {"type": "string"},
"else": {"$ref": "#/definitions/root"},
"enum": {"items": true, "type": "array"},
"examples": {"items": true, "type": "array"},
"exclusiveMaximum": {"type": "number"},
"exclusiveMinimum": {"type": "number"},
"format": {"type": "string"},
"if": {"$ref": "#/definitions/root"},
"items": {
"anyOf": [
{"$ref": "#/definitions/root"},
{"$ref": "#/definitions/schemaArray"},
],
"default": true,
},
"maxItems": {"$ref": "#/definitions/nonNegativeInteger"},
"maxLength": {"$ref": "#/definitions/nonNegativeInteger"},
"maxProperties": {"$ref": "#/definitions/nonNegativeInteger"},
"maximum": {"type": "number"},
"minItems": {"$ref": "#/definitions/nonNegativeIntegerDefault0"},
"minLength": {"$ref": "#/definitions/nonNegativeIntegerDefault0"},
"minProperties": {"$ref": "#/definitions/nonNegativeIntegerDefault0"},
"minimum": {"type": "number"},
"multipleOf": {"exclusiveMinimum": 0, "type": "number"},
"not": {"$ref": "#/definitions/root"},
"oneOf": {"$ref": "#/definitions/schemaArray"},
"pattern": {"format": "regex", "type": "string"},
"patternProperties": {
"additionalProperties": {"$ref": "#/definitions/root"},
"default": {},
"propertyNames": {"format": "regex"},
"type": "object",
},
"properties": {
"additionalProperties": {"$ref": "#/definitions/root"},
"default": {},
"type": {"enum": ["object", ["object", "null"]]},
},
"propertyNames": {"$ref": "#/definitions/root"},
"readOnly": {"default": false, "type": "boolean"},
"required": {"$ref": "#/definitions/stringArray"},
"then": {"$ref": "#/definitions/root"},
"title": {"type": "string"},
"type": {
"anyOf": [
{"$ref": "#/definitions/simpleTypes"},
{
"items": {"$ref": "#/definitions/simpleTypes"},
"minItems": 1,
"type": "array",
"uniqueItems": true,
},
]
},
"uniqueItems": {"default": false, "type": "boolean"},
},
"required": ["codec"],
"title": "Core schema meta-schema",
"type": ["object", "boolean"],
}