This primer introduces JSON Structure, a modern, strictly typed data definition language that resembles JSON Schema, but has very different basic design principles.
JSON Structure focuses on defining data types with clear, deterministic constraints while supporting extensions for precise representations of numbers, dates, and more.
Common programming language concepts like reuse and composition are supported directly in the schema language, but while avoiding introduction of the entire complexity of object-oriented programming.
Conditional validation of JSON data against schemas with composition constructs like anyOf or allOf is availble in JSON Structure quite similar to JSON Schema, has been split out into optional extensions, allowing simple data definitions to remain lightweight and easy to understand.
1. Introduction
There are rapidly growing needs for a standardized (IETF RFC) schema language that can describe data types and structures and whose definitions map cleanly to programming language types and database constructs as well as into the popular JSON data encoding. The type model needs to reflect the needs of modern applications and it must allow rich annotations with semantic information that can be evaluated and understood by developers and by large language models (LLMs).
JSON Schema has been in development since ca. 2009 and has gone through several iterations. The industry has largely settled on “Draft 7” of JSON Schema, with subsequent releases seeing comparatively weak adoption. There’s substantial frustration that many developers have with JSON Schema because they try to use it for scenarios that it was not designed for. JSON Schema is a powerful document validation tool, but it is not a data definition language. Frustration with JSON Schema is widespread across the industry—and yet it remains popular due to lack of good alternatives.
Apache Avro is an alternative that has gained traction in the data community because it’s much easier to create tooling on top of. However, Avro’s type system is limited and its syntax is unfamiliar to most developers.
JSON Structure aims to combine the tooling-friendliness of Avro with the familiar type definition shapes of JSON Schema while providing an overall broader type system and data documentation/qualification capabilities that provide richer context for AI/LLMs trying to understand data structures.
While JSON Schema focuses on and excels at document validation, JSON Structure focuses on being a strong data definition language.
There are two major use-case scenarios for schema languages in general:
- Validating JSON data against a schema to ensure conformity to a specific structure and constraints.
- Declaring data types and structures in a machine-readable format that can be used to generate code, documentation, or other artifacts across platforms and languages.
JSON Schema provides powerful facilities for the first use-case. JSON Structure prioritizes the second use-case while still supporting the validation scenarios similar to JSON Schema through extensions.
With JSON Structure, we’ve designed a type system that addresses common challenges:
- Clear mapping to programming language types
- Support for more precise numeric types and date/time representations
- Modular approach to extensions
- Simplified cross-references between schema documents
- Straightforward reuse patterns for types
This approach better supports code generation, database mappings, and integration with modern programming languages, while keeping the validation capabilities as optional extensions for those who need them.
2. Design Principles
JSON Structure is designed with the following principles in mind:
- Scope: JSON Structure “Core” is meant to be a sufficient language for a “schema first” approach to defining application data structures in “polyglot” environments where such definitions are shared across application modules and tiers and databases and caches and other data stores. It is not a goal to be able to capture, say, all intricacies of a C++ class and express those. In that way, JSON Structure definitions are meant to be authoritative. Tools should be able to map from JSON Structure to programming language constructs and, within reasonable bounds, to database structures without losing structural information, but the reverse might not always be true.
- Relation to JSON: JSON Structure is expressed in JSON and has built-in rules for how types are mapped to JSON encoding, but the data definitions can be seen as being independent of JSON. You can declare an object type in JSON Structure and use that type to code-generate a record class for an Object-Relational-Mapping framework and a corresponding database table without JSON encoding ever playing a role.
- Familiarity: Millions of software developers recognize declarations of the shape “type”:“object”, “properties”: {…} as what a “schema” should look like and it’s often the only formal schema model they a have encountered. In spite of its shortcomings as a data definition language, JSON Schema’s most basic expressions for to declare a type are etched into people’s minds and any improvement that does not pick people up from where they are´will have it harder to gain substantial traction.
- Simplicity: JSON Structure is designed to be simple and easy to understand. The schema language is designed to be easy to read and write, with a focus on clear and concise definitions. The type system is meant to easily align with concepts people use and need in their programming and database work, including safe numeric expressions and date and time expressions.
- Pragmatism: JSON Structure is designed to be a pragmatic data definition language for application developers and not a language for metadata geeks. That means that when there is tension between providing type definitions for application use-cases and being able to describe or validate any imaginable JSON expression, the application use-cases win.
- Optional Complexity: JSON Structure “Core” documents are always self-contained and simple to understand and process. If the user needs further capabilities like imports of definitions from external files, semantic annotations, or complex validation hints, they can opt into those by using companion specifications, potentially at the expense of compatibility with implementations of JSON Structure that are restricted to the “Core” specification.
3. Key Concepts
JSON Structure is designed to look and feel very much like the JSON Schema
drafts you already know, but some rules have been tightened up to make it easier to understand and use. Therefore, existing JSON Schema documents may need to be updated to conform to the new rules.
3.1. Differences to JSON Schema Drafts
- Strict Typing: Every schema must explicitly specify its data type. For compound types (objects, arrays, sets), additional required metadata like a name and other required property definitions help enforce structured data.
- Identifiers: Names of types and properties are restricted to the regular expression
[A-Za-z_][A-Za-z0-9_]*to make them easier to map to code and database constructs. Map keys may additionally contain:,-, and.and may start with a digit. - Extended Types: In addition to JSON primitives such as string, number, boolean, and null, JSON Structure Core supports many extended primitive types (e.g.,
int32,int64,decimal,date,uuid) for high precision or format-specific data. - Compound Types: The compound types have been extended to include
set,map, andtuple- Object: Define structured data with a required
nameand a set ofproperties. - Array: List of items where the
itemsattribute references a declared type without inline compound definitions. - Set: An unordered collection of unique elements.
- Map: A collection of key-value pairs where keys are strings and values are of a declared type.
- Tuple: A fixed-length array of elements where each element is of a declared type. It’s a more compact alternative to objects where the property names are replaced by the position in the tuple. Arrays or maps of tuples are especially useful for time-series data.
- Choice: A discriminated union of types, where one of the types is selected based on a discriminator property. The
choicekeyword is used to define the union and theselectorproperty is used to specify the discriminator property.
- Object: Define structured data with a required
- Namespaces: Namespaces are a formal part of the schema language, allowing for more modular and deterministic schema definitions. Namespaces are used to scope type definitions.
- Cross-Referencing: The
$refkeyword has been limited to referencing named types that exist within the same document. It can no longer reference and insert arbitrary JSON nodes and it can no longer reference external documents. To reuse types from other documents, you now need to use the$importkeyword from the optional import spec to import the types you need. Once imported, you can reference types with$ref.
3.2. Extensibility Model
JSON Structure is designed to be extensible. Any schema can be extended with custom keywords given that those do not conflict with the core schema language or companion specifications that are “activated” for the schema document. It’s recommended for custom keywords to have a compnay- or project-specific prefix to avoid such collisions. It’s not required to declare a formal meta-schema to use custom keywords, but it’s recommended to do so.
JSON Structure is designed to be modular. The core schema language is defined in the JSON Structure Core document. Companion specifications provide additional features and capabilities that can be used to extend the core schema language.
The abstract and $extends keywords enable controlled type extension, supporting basic object-oriented-programming-style inheritance while not permitting subtype polymorphism where a sub-type value can be assigned a base-typed property. This approach avoids validation complexities and mapping issues between JSON schemas, programming types, and databases.
There are two types of formal extensions: extensible types and add-in types.
3.3. Extensible Types
It’s fairly common that different types share a common basic set of properties. In JSON Schema itself, the description property is a good example of a property that is shared across all schema and non-schema objects.
An extensible type is declared as abstract and provides a set of common definitions to be shared by extensions. For example, a base type AddressBase MAY be extended by StreetAddress and PostOfficeBoxAddress via $extends. Because it’s abstract, AddressBase cannot be used directly anywhere as a type, however. Address extends AddressBase without adding any additional properties.
Example:
{
"$schema": "🔗,
"definitions" : {
"AddressBase": {
"abstract": true,
"type": "object",
"properties": {
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string" }
}
},
"StreetAddress": {
"type": "object",
"$extends": "#/definitions/AddressBase",
"properties": {
"street": { "type": "string" }
}
},
"PostOfficeBoxAddress": {
"type": "object",
"$extends": "#/definitions/AddressBase",
"properties": {
"poBox": { "type": "string" }
}
},
"Address": {
"type": "object",
"$extends": "#/definitions/AddressBase"
}
}
}
3.4. Add-in Types
Add-in types allow modifying any existing type in a schema with additional properties or constraints. Add-ins are offered to the instance document author through the $offers keywords in the schema document. The instance document author can then enable the add-in by referencing the add-in name in the $uses keyword. When the instance document does so, the add-in properties are injected into the designated schema types before the schema is evaluated. Enabled add-ins are treated as if they were part of the schema from the beginning.
Add-ins can also be enabled in meta-schemas such that they are always applied to schemas that are based on the meta-schema.
A add-in type is declared as abstract and $extends a specific type that does not need to be abstract. For example, a add-in type DeliveryInstructions might be applied to any StreetAddress types in a document:
{
"$schema": "🔗,
"$id": "🔗,
"$root": "#/definitions/StreetAddress",
"$offers": {
"DeliveryInstructions": "#/definitions/DeliveryInstructions"
},
"definitions" : {
"StreetAddress": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string" }
}
},
"DeliveryInstructions": {
"abstract": true,
"type": "object",
"$extends": "#/definitions/StreetAddress",
"properties": {
"instructions": { "type": "string" }
}
}
}
}
Add-ins are applied to a schema by referencing the add-in name in the $uses keyword that is available only in instance documents. The $uses keyword is a set of add-in names that are applied to the schema for the document.
{
"$schema": "🔗,
"$uses": ["DeliveryInstructions"],
"street": "123 Main St",
"city": "Anytown",
"state": "QA",
"zip": "00001",
"instructions": "Leave at the back door"
}
3.5. Reusing Types across different Schema Documents
The prior versions of JSON Schema in effect allowed for $ref to reference arbitrary JSON nodes from the same or external document, which made references very difficult to understand and process, especially when the references were deep links into external schema documents and/or schema documents were cross-referenced using relative URIs to avoid committing to an absolute schema location.
JSON Structure has a more controlled approach to limit that complexity.
- The
$refkeyword can only reference named types that exist within the same document. It can no longer reference and insert arbitrary JSON nodes and it can no longer reference external documents. - To reuse types from other documents, you now need to use the
$importkeyword from the optional import spec to import the types you need into the scope of the schema document. Once imported, you use the imported types as if they were declared in the document. To avoid conflicts, you can import external types into a namespace. - Since types are imported and then referenced as if they were declared in the document, it’s also possible and permitted to “shadow” imported types with local definitions by explicitly declaring a type with the same name and namespace as as the imported type.
3.6. Core and Companion Specifications
The following documents are part of this JSON Structure proposal:
- JSON Structure Core: Defines the core schema language for declaring data types and structures.
- JSON Structure Alternate Names and Descriptions: Provides a mechanism for declaring alternate names and symbols for types and properties, including for the purposes of internationalization.
- JSON Structure Symbols, Scientific Units, and Currencies: Defines keywords for specifying symbols, scientific units, and currency codes on types and properties.
- JSON Structure Conditional Composition: Defines a set of conditional composition rules for evaluating schemas.
- JSON Structure Validation: Specifies extensions to the core schema language for declaring validation rules.
- JSON Structure Import: Defines a mechanism for importing external schemas and definitions into a schema document.
3.7. Meta-Schemas
Meta-schemas are JSON Structure documents that define the structure and constraints of schema documents themselves. Meta-schemas do not have special constructs beyond what is available in the core schema language.
A meta-schema is referenced in a schema document using the $schema keyword. The meta-schema defines the constraints that the schema document must adhere to.
The value of the $schema keyword is a URI that corresponds to the $id declared in the meta-schema document. The URI should be a resolvable URL that points to the meta-schema document, but for well-known meta-schemas, a schema processor will typically not actually fetch the meta-schema document. Instead, the schema processor will use the URI to identify the meta-schema and validate the schema document against its copy of the meta-schema.
A meta-schema can build on another meta-schema by $importing all of its definitions and then adding additional definitions or constraints or by shadowing definitions from the imported meta-schema.
The “core” meta-schema formally defines the elements described in the JSON Structure Core document. The “$id” of the core meta-schema is 🔗.
The “extended” meta-schema extends the core meta-schema with all additional features and capabilities provided by the companion specifications and offers those features to schema authors. The “$id” of the extended meta-schema is 🔗.
The “validation” meta-schema enables all add-ins defined in the extended meta-schema.
4. Using Structure Core
This section introduces JSON Structure by example.
4.1. Example: Declaring a simple object type
Here is an example of a simple object type definition:
{
"$schema": "🔗,
"type": "object",
"name": "Person",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"dateOfBirth": { "type": "date" }
},
"required": ["firstName", "lastName"]
}
If you are familiar with JSON Schema, you will instantly recognize the structure of this schema. The type attribute specifies that this is an object type. The properties attribute lists the properties of the object, and the required attribute lists the properties that are required.
There are a few differences from prior version of JSON Schema. The name attribute is new and is required for the root type. The type attribute is also required and no longer implied to be object if not present. You may also notice that the “dateOfBirth” property uses the new date type, which is an extended native type.
4.2. Example: Declaring Primitive and Extended Types
Below is an example schema that defines a simple profile with a few more extended types:
{
"$schema": "🔗,
"type": "object",
"name": "UserProfile",
"properties": {
"username": { "type": "string" },
"dateOfBirth": { "type": "date" },
"lastSeen": { "type": "datetime" },
"score": { "type": "int64" },
"balance": { "type": "decimal", "precision": 20, "scale": 2 },
"isActive": { "type": "boolean" }
},
"required": ["username", "birthdate"]
}
The int64 type is an extended type that represents a 64-bit signed integer. The decimal type is another extended type that represents a decimal number with a specified precision and scale. The datetime type is an extended type that represents a date and time value.
5. Example: Declaring inline compound types
This is an example of a type that is declared inline. This is useful for compound types that are not reused elsewhere in the schema. The address property of the UserProfile type references the inline Address type. This type cannot be referenced from other types in the schema.
{
"$schema": "🔗,
"type": "object",
"name": "UserProfile",
"properties": {
"username": { "type": "string" },
"dateOfBirth": { "type": "date" },
"lastSeen": { "type": "datetime" },
"score": { "type": "int64" },
"balance": { "type": "decimal", "precision": 20, "scale": 2 },
"isActive": { "type": "boolean" },
"address": {
"type": "object",
"name": "Address",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string" }
},
"required": ["street", "city", "state", "zip"]
}
},
"required": ["username", "birthdate"]
}
5.1. Example: Declaring reusable types in definitions
To define reusable types, you can use the definitions keyword to define types that can be referenced by other types in the same document. Here is an example:
{
"$schema": "🔗,
"type": "object",
"name": "UserProfile",
"properties": {
"username": { "type": "string" },
"dateOfBirth": { "type": "date" },
"lastSeen": { "type": "datetime" },
"score": { "type": "int64" },
"balance": { "type": "decimal", "precision": 20, "scale": 2 },
"isActive": { "type": "boolean" },
"address": { "type" : { "$ref": "#/definitions/Address" } }
},
"required": ["username", "birthdate"],
"definitions": {
"Address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string" }
},
"required": ["street", "city", "state", "zip"]
}
}
}
In this example, the Address type is declared in the definitions section and can be referenced by other types in the same document using the $ref keyword. Mind that the $ref keyword can now only reference types declared in the definitions section of the same document. The keyword can only be used where a type is expected.
5.2. Example: Structuring types with namespaces
Namespaces are used to scope type definitions. Here is an example of how to use namespaces to structure your types, with two differing Address types:
{
"$schema": "🔗,
"type": "object",
"name": "UserProfile",
"properties": {
"username": { "type": "string" },
"dateOfBirth": { "type": "date" },
"networkAddress": { "type" : { "$ref": "#/definitions/Network/Address" } },
"physicalAddress": { "type": { "$ref": "#/definitions/Physical/Address" } }
},
"required": ["username", "birthdate"],
"definitions": {
"Network": {
"Address": {
"type": "object",
"properties": {
"ipv4": { "type": "string" },
"ipv6": { "type": "string" }
}
}
},
"Physical": {
"Address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string" }
},
"required": ["street", "city", "state", "zip"]
}
}
}
}
5.3. Example: Using an Array Type
This example shows how to declare an array of strings, which is not much different from defining an object:
{
"$schema": "🔗,
"type": "array",
"items": { "type": "string" }
}
You can also declare an array of a locally declared compound type, but you can not reference the type from elsewhere in the schema:
{
"$schema": "🔗,
"type": "array",
"items": {
"type": "object",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"dateOfBirth": { "type": "date" }
},
"required": ["firstName", "lastName"]
}
}
To declare an array of a reusable type, you can use the $ref keyword:
{
"$schema": "🔗,
"type": "array",
"items": { "type" : { "$ref": "#/definitions/Person" } },
"definitions": {
"Person": {
"type": "object",
"name": "Person",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"dateOfBirth": { "type": "date" }
},
"required": ["firstName", "lastName"]
}
}
}
5.4. Example: Declaring Maps
This example shows how to declare a map of strings to Color objects:
{
"$schema": "🔗,
"type": "map",
"values": { "type": { "$ref": "#/definitions/Color" } },
"definitions": {
"Color": {
"type": "object",
"name": "Color",
"properties": {
"red": { "type": "int32" },
"green": { "type": "int32" },
"blue": { "type": "int32" }
},
"required": ["red", "green", "blue"]
}
}
}
Instance data for this schema might look like this:
{
"rose": { "red": 255, "green": 0, "blue": 0 },
"sky": { "red": 0, "green": 191, "blue": 255 },
"grass": { "red": 0, "green": 128, "blue": 0 },
"sun": { "red": 255, "green": 215, "blue": 0 },
"cloud": { "red": 255, "green": 255, "blue": 255 },
"moon": { "red": 192, "green": 192, "blue": 192 }
}
5.5. Example: Declaring Sets
This example shows how to declare a set of strings:
{
"$schema": "🔗,
"type": "set",
"items": { "type": "string" }
}
Sets differ from arrays in that they are unordered and contain only unique elements. The schema above would match the following instance data:
["apple", "banana", "cherry"]
5.6. Example: Declaring Tuples
Tuples are fixed-length arrays with named elements, declared via the tuple keyword.
{
"$schema": "🔗,
"type": "tuple",
"name": "PersonTuple",
"properties": {
"firstName": { "type": "string" },
"age": { "type": "int32" }
},
"tuple": ["firstName", "age"]
}
An instance of this tuple type:
["Alice", 30]
5.7. Example: Declaring Choice Types
Choice types define discriminated unions via the choice keyword. Two forms are supported:
5.8. Tagged Unions
Tagged unions represent the selected type as a single-property object:
{
"$schema": "🔗,
"type": "choice",
"name": "StringOrNumber",
"choices": {
"string": { "type": "string" },
"int32": { "type": "int32" }
}
}
Valid instances:
{ "string": "Hello" }
{ "int32": 42 }
5.9. Inline Unions
Inline unions extend a common abstract base type and use a selector property:
{
"$schema": "🔗,
"type": "choice",
"name": "AddressChoice",
"$extends": "#/definitions/Address",
"selector": "addressType",
"choices": {
"StreetAddress": { "type": { "$ref": "#/definitions/StreetAddress" } },
"PostOfficeBoxAddress": { "type": { "$ref": "#/definitions/PostOfficeBoxAddress" } }
},
"definitions": {
"Address": {
"abstract": true,
"type": "object",
"properties": {
"city": { "type": "string" },
"state":{ "type": "string" },
"zip": { "type": "string" }
}
},
"StreetAddress": {
"type": "object",
"$extends": "#/definitions/Address",
"properties": { "street": { "type": "string" } }
},
"PostOfficeBoxAddress": {
"type": "object",
"$extends": "#/definitions/Address",
"properties": { "poBox": { "type": "string" } }
}
}
}
Instance of this inline union:
{
"addressType": "StreetAddress",
"street": "123 Main St",
"city": "AnyCity",
"state": "AS",
"zip": "11111"
}
6. Using Companion Specifications
The JSON Structure Core specification is designed to be extensible through companion specifications that provide additional features and capabilities.
The extended schema that includes all companion specifications is identified by the 🔗 URI. Each companion specification is identified by a unique identifier that can be used in the $uses attribute to activate the companion specification for the schema document.
The feature identifiers for the companion specifications are:
JSONStructureAlternateNames: Alternate names and descriptions for properties and types.JSONStructureUnits: Symbols, scientific units, and currencies for numeric properties.JSONStructureImports: Importing types from other schema documents.JSONStructureValidation: Validation rules for JSON data.JSONStructureConditionalComposition: Conditional composition and validation rules.
6.1. Example: Using the altnames Keyword
The JSON Structure Alternate Names and Descriptions companion specification introduces the altnames keyword to provide alternate names for properties and types. Alternate names provide additional mappings that schema processors may use for encoding, decoding, or user interface display.
Here is an example of how to use the altnames keyword:
{
"$schema": "🔗,
"$uses": ["JSONStructureAlternateNames"],
"Person": {
"type": "object",
"altnames": {
"json": "person_data",
"lang:en": "Person",
"lang:de": "Person"
},
"properties": {
"firstName": {
"type": "string",
"altnames": {
"json": "first_name",
"lang:en": "First Name",
"lang:de": "Vorname"
}
},
"lastName": {
"type": "string",
"altnames": {
"json": "last_name",
"lang:en": "Last Name",
"lang:de": "Nachname"
}
}
},
"required": ["firstName", "lastName"]
}
}
Each named type or property in the schema has been given an altnames attribute that provides alternate names for the type or property.
The json key is used to specify alternate names for JSON encoding, meaning that if the schema is used to encode or decode JSON data, the alternate key MUST be used instead of the name in the schema.
Keys beginning with lang: are reserved for providing localized alternate names that can be used for user interface display. Additional keys can be used for custom purposes, subject to no conflicts with reserved keys or prefixes.
6.2. Example: Using the altenums Keyword
The JSON Structure Alternate Enumerations companion specification introduces the altenums keyword to provide alternative representations for enumeration values defined by a type using the enum keyword. Alternate symbols allow schema authors to map internal enum values to external codes or localized display symbols.
Here is an example of how to use the altenums keyword:
{
"$schema": "🔗,
"$uses": ["JSONStructureAlternateNames"],
"type": "object",
"name": "Color",
"properties": {
"name": { "type": "string" },
"value": {
"type": "string",
"enum": ["red", "green", "blue"],
"altenums": {
"lang:en": {
"red": "Red",
"green": "Green",
"blue": "Blue"
},
"lang:de": {
"red": "Rot",
"green": "Grün",
"blue": "Blau"
}
}
}
}
}
In this example, the value property has an enum attribute that defines the possible values for the property. The altenums attribute provides alternative names for each enumeration value. The lang:en key provides English names for the enumeration values, and the lang:de key provides German names.
6.3. Example: Using the unit Keyword
The JSON Structure Symbols, Scientific Units, and Currencies companion specification introduces the unit keyword to provide a standard way to specify the unit of measurement for numeric properties. The unit keyword allows schema authors to specify the unit of measurement for numeric properties and provides a standard way to encode and decode numeric values with units.
Here is an example of how to use the unit keyword:
{
"$schema": "🔗,
"$uses": ["JSONStructureUnits"],
"type": "object",
"name": "Pressure",
"properties": {
"value": { "type": "number", "unit": "Pa" }
}
}
In this example, the value property has a unit attribute that specifies the unit of measurement for the property. The unit of measurement is specified as a string value. In this case, the unit of measurement is “Pa” for Pascals.
6.4. Example: Using the currency Keyword
The JSON Structure Symbols, Scientific Units, and Currencies companion specification also introduces the currency keyword to provide a standard way to specify the currency for monetary properties. The currency keyword allows schema authors to specify the currency for monetary properties and provides a standard way to encode and decode monetary values with currencies.
Here is an example of how to use the currency keyword:
{
"$schema": "🔗,
"$uses": ["JSONStructureUnits"],
"type": "object",
"name": "Price",
"properties": {
"value": { "type": "decimal", "precision": 20, "scale": 2, "currency": "USD" }
}
}
In this example, the value property has a currency attribute that specifies the currency for the property. The currency is specified as a string value. In this case, the currency is “USD” for US Dollars.
7. Using Validation
The companion specifications for conditional composition and validation provide additional constructs for defining conditional validation rules and composing that resemble those found in prior versions of JSON Schema. However, those have been split out into optional extensions to keep the core schema language simple.
7.1. Example: Using Conditional Composition
The JSON Structure Conditionals companion specification introduces conditional composition constructs for combining multiple schema definitions. In particular, this specification defines the semantics, syntax, and constraints for the keywords allOf, anyOf, oneOf, and not, as well as the if/then/else conditional construct.
The specification has several examples that show how to use the conditional composition keywords.
7.2. Example: Using Validation Rules
The JSON Structure Validation companion specification introduces additional validation rules for JSON data.
8. SDK and Tooling Ecosystem
A schema language without tooling remains theoretical. For developers to adopt JSON Structure, they need to be able to tell whether schemas are valid, whether documents conform to a given schema, and they need to be able to turn schemas into data structures they can code against.
The JSON Structure SDKs provide native implementations for schema validation (is this schema well-formed?) and instance validation (does this JSON document conform to this schema?) across all major programming languages:
| Language | Package | Install |
|---|---|---|
| Python | json-structure | pip install json-structure |
| .NET/C# | JsonStructure | dotnet add package JsonStructure |
| Java | json-structure | Maven Central |
| TypeScript/JS | @aspect/json-structure | npm install @aspect/json-structure |
| Go | github.com/json-structure/sdk/go | go get github.com/json-structure/sdk/go |
| Rust | json-structure | cargo add json-structure |
| C/C++ (C99) | Build from repo | CMake |
For languages with runtime introspection (Java, .NET/C#, Python), the SDKs also include schema exporters that produce JSON Structure schemas from existing runtime data structures—useful for adopting JSON Structure incrementally in existing codebases.
The SDK repository is at Official SDKs for JSON Structure schema and instance validation - json-structure/sdk
GitHubGitHub - json-structure/sdk: Official SDKs for JSON Structure schema and instance validation
9. Visual Studio Code Extension
The JSON Structure extension for Visual Studio Code, built on the TypeScript SDK, provides a first-class editing experience for .struct.json schema files:
- Schema validation: Real-time feedback on schema syntax and structure
- Instance validation: Validate JSON documents against JSON Structure schemas
- IntelliSense: Auto-completion for keywords, types, and references
- Hover documentation: Contextual help for all schema elements
Install from the VS Code Marketplace or search for “JSON Structure” in the Extensions view.
10. Structurize: Schema Conversion and Code Generation
The Structurize tool (a persona of the Avrotize tool) enables robust, predictable conversion between numerous schema formats and generates code for multiple target languages. JSON Structure serves as a second “pivot” schema model for this tool, meaning you can convert from many formats to JSON Structure, and from JSON Structure to many targets—preserving the full richness of the type model.
Installation
pip install structurize
# or
pip install avrotize
For the moment, the tools have an identical set of commands, but they may eventually be split. There will eventually be a bundled installer (as for the Azure CLI) so that you don’t need Python preinstalled.
Example Conversions
| From | To | Command |
|---|---|---|
| JSON Structure | C# | structurize jsonstruct2cs schema.struct.json --out Models.cs |
| JSON Structure | Python | structurize jsonstruct2py schema.struct.json --out models.py |
| JSON Structure | Java | structurize jsonstruct2java schema.struct.json --out-dir ./src |
| JSON Structure | Go | structurize jsonstruct2go schema.struct.json --out models.go |
| JSON Structure | C++ | structurize jsonstruct2cpp schema.struct.json --out models.hpp |
| JSON Structure | Protobuf | structurize jsonstruct2proto schema.struct.json --out schema.proto |
| JSON Structure | PostgreSQL | structurize jsonstruct2pgsql schema.struct.json --out schema.sql |
There are many more database dialects and target formats available. The full docs are in the Avrotize repo README.
Chaining Conversions
The tool is built such that you can pipe conversions, so you could go from ASN.1 via Avro to JSON Structure to C# with minimal loss of type info:
# ASN.1 → Avro → JSON Structure → C#
structurize asn2avro schema.asn | structurize avro2jsonstruct | structurize jsonstruct2cs --out Models.cs
11. Resources and Next Steps
Specifications (IETF Internet Drafts)
All JSON Structure specifications are published as IETF Internet Drafts:
- JSON Structure Core - The core schema language
- JSON Structure Import - Importing types from other documents
- JSON Structure Alternate Names - Alternate names and i18n
- JSON Structure Units - Symbols, scientific units, currencies
- JSON Structure Validation - Validation constraints
- JSON Structure Conditional Composition - allOf, anyOf, oneOf, if/then/else
All drafts are available at: 🔗
SDK and Tools
| Resource | Link |
|---|---|
| SDK Repository | GitHub - json-structure/sdk: Official SDKs for JSON Structure schema and instance validationOfficial SDKs for JSON Structure schema and instance validation - json-structure/sdk |
| VS Code Extension | Search “JSON Structure” in VS Code Marketplace |
| Structurize/Avrotize | pip install structurize or GitHub - clemensv/avrotize: Avrotize is a command-line tool for converting data structure definitions between different schema formats, using Apache Avro Schema as the integration schema model.Avrotize is a command-line tool for converting data structure definitions between different schema formats, using Apache Avro Schema as the integration schema model. - clemensv/avrotize |
Sample Schemas
The samples folder in this repository contains working examples organized by specification:
samples/core/- Core specification examples with validation scriptssamples/import/- Import specification examples
Getting Started
- Install the VS Code extension for editing support with validation and IntelliSense
- Install the SDK for your language to validate schemas and instances programmatically
- Try the samples to understand the schema patterns
- Use Structurize to convert existing schemas or generate code for your target language
