Specification scope: Core only.
Tool: Structurize is the JSON Structure-focused command-line interface and conversion toolkit from the Avrotize project.
A C# class, a .proto file, and a SQL script can all represent the same
shipment, but each expresses the model through a different target type system.
One preserves a UUID, another exposes a string, and the database may accept
null. None of those artifacts can serve as the neutral definition without
making its target’s constraints and omissions authoritative for every other
consumer.
JSON Structure provides that target-independent definition. It records the intended types, presence rules, and collection semantics once, before any language, protocol, or database narrows them. In the Structurize version pinned below, the listed commands read that schema and project it into target artifacts. A target may preserve a declaration, translate it into a native type, or represent it more weakly; that result describes the projection, not a revision of the model. The generated outputs remain useful, but the JSON Structure schema is the contract from which they can be rebuilt.
The examples use Structurize 3.9.0. Install the pinned release from PyPI:
python -m pip install structurize==3.9.0
Put the decisions in the contract
Consider a fulfillment service that plans a shipment after an order has been allocated to a warehouse. The contract needs an order identifier, a carrier service, a dispatch time, a monetary value, and a collection of handling tags.
{
"$schema": "https://json-structure.org/meta/core/v0/#",
"$id": "https://example.com/schemas/shipment-plan",
"name": "ShipmentPlan",
"type": "object",
"description": "A shipment plan prepared after an order is allocated to a warehouse.",
"properties": {
"orderId": {
"type": "uuid",
"description": "The order for which the shipment is planned."
},
"carrierService": {
"type": "string",
"description": "The carrier service selected to transport the shipment.",
"maxLength": 80
},
"dispatchAt": {
"type": "datetime",
"description": "The time at which the shipment is scheduled to leave the warehouse."
},
"declaredValue": {
"type": "decimal",
"description": "The monetary value declared for carriage.",
"precision": 12,
"scale": 2
},
"handlingTags": {
"type": "set",
"description": "Handling instructions that apply to the shipment.",
"items": {
"type": "string",
"description": "A handling instruction assigned to the shipment."
}
}
},
"required": [
"orderId",
"carrierService",
"dispatchAt",
"declaredValue",
"handlingTags"
],
"additionalProperties": false
}
These declarations are not hints to a generator. uuid,
datetime, decimal, and set are types in the
contract. required fixes presence. additionalProperties closes the object against undeclared
fields.
The instance remains ordinary JSON:
{
"orderId": "c28788fa-73bd-4b39-a209-2f75f82c6653",
"carrierService": "priority-ground",
"dispatchAt": "2026-08-25T16:30:00Z",
"declaredValue": "184.50",
"handlingTags": ["fragile", "keep-dry"]
}
The schema carries the information that the JSON text cannot carry by itself.
Every schema element also has a description, including the items schema
inside handlingTags. The array under handlingTags is unordered and
duplicate-free because its declared type is set. The quoted amount belongs
to a base-10 decimal domain. The identifier is more specific than arbitrary
text.
Project views, do not maintain copies
Structurize 3.9.0 exposes the
direct JSON Structure projections used here. The commands and options are defined in the
3.9.0 CLI manifest.
From the same shipment-plan.struct.json, a build can produce several views:
New-Item -ItemType Directory -Force generated | Out-Null
structurize s2md shipment-plan.struct.json --out generated/shipment-plan.md
structurize s2p shipment-plan.struct.json --out generated/proto
structurize s2sql shipment-plan.struct.json --out generated/shipment-plan.sql --dialect postgres
structurize s2cs shipment-plan.struct.json --out generated/dotnet --namespace Fulfillment.Contracts
structurize s2java shipment-plan.struct.json --out generated/java --package com.example.fulfillment
Generated output: shipment-plan.md
# shipment-plan.struct
A shipment plan prepared after an order is allocated to a warehouse.
**Schema ID:** `https://example.com/schemas/shipment-plan`
## Objects
### ShipmentPlan
A shipment plan prepared after an order is allocated to a warehouse.
**Properties:**
- **orderId** (required): `uuid`
- Description: The order for which the shipment is planned.
- **carrierService** (required): `string`
- Description: The carrier service selected to transport the shipment.
- Constraints: maxLength: 80
- **dispatchAt** (required): `datetime`
- Description: The time at which the shipment is scheduled to leave the warehouse.
- **declaredValue** (required): `decimal`
- Description: The monetary value declared for carriage.
- Constraints: precision: 12, scale: 2
- **handlingTags** (required): set<`string`>
- Description: Handling instructions that apply to the shipment.
For a complex multi-file code-generation example, use the prebuilt Avrotize Inventory to C# gallery example, which shows its source schema and complete output tree. The small schema here emits this representative file from its generated C# project:
Generated output: ShipmentPlan.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace Fulfillment.Contracts
{
/// <summary>
/// A shipment plan prepared after an order is allocated to a warehouse.
/// </summary>
public sealed partial class ShipmentPlan
{
/// <summary>
/// The order for which the shipment is planned.
/// </summary>
public required Guid orderId { get; set; }
/// <summary>
/// The carrier service selected to transport the shipment.
/// </summary>
[System.ComponentModel.DataAnnotations.StringLength(80)]
public required string carrierService { get; set; }
/// <summary>
/// The time at which the shipment is scheduled to leave the warehouse.
/// </summary>
public required DateTimeOffset dispatchAt { get; set; }
/// <summary>
/// The monetary value declared for carriage.
/// </summary>
public required decimal declaredValue { get; set; }
/// <summary>
/// Handling instructions that apply to the shipment.
/// </summary>
public required HashSet<string> handlingTags { get; set; }
/// <summary>
/// Default constructor
/// </summary>
public ShipmentPlan()
{
}
/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
public override bool Equals(object? obj)
{
if (obj is not ShipmentPlan other) return false;
return this.orderId == other.orderId
&& this.carrierService == other.carrierService
&& this.dispatchAt == other.dispatchAt
&& this.declaredValue == other.declaredValue
&& this.handlingTags.SequenceEqual(other.handlingTags);
}
/// <summary>
/// Serves as the default hash function.
/// </summary>
public override int GetHashCode()
{
return HashCode.Combine(orderId, carrierService, dispatchAt, declaredValue, handlingTags.Aggregate(0, (acc, item) => HashCode.Combine(acc, item)));
}
}
}
s2md emits a Markdown contract view, s2p adapts the model to Protocol
Buffers, and s2sql projects it into the selected SQL dialect. The code
generators create language-facing APIs; the same manifest also defines s2ts,
s2go, and s2rust.
Each output answers a different consumer’s question. None gains authority over the schema merely because people read it more often.
A projection can be narrower
Projection is not photocopying. Target systems have different type systems, collection models, naming rules, and representation constraints. A generator must sometimes choose the closest available construct.
Protocol Buffers, for example, has repeated fields but no native set field. SQL dialects differ in their types and in how they represent nested collections. Language targets can preserve a set directly, but their concrete types differ. Those differences do not make the source contract vague. They make the projection policy visible.
This distinction matters during review. If handlingTags appears as a
repeated field in the generated .proto, changing the JSON Structure type from
set to array merely to make both files look alike would reverse the
authority. The source says membership is unique and order has no meaning. A
target that cannot express that fact needs enforcement at its boundary or a
documented loss of semantics.
The generated artifact is evidence of how one tool version projected one contract. It is not a second contract.
Regeneration makes drift observable
Treat the projections like compiler output. Keep the command line and tool version in the build, then regenerate from a known schema revision.
New-Item -ItemType Directory -Force generated | Out-Null
structurize s2md shipment-plan.struct.json --out generated/shipment-plan.md
structurize s2p shipment-plan.struct.json --out generated/proto
structurize s2sql shipment-plan.struct.json --out generated/shipment-plan.sql --dialect postgres
Generated output: proto.proto
syntax = "proto3";
package proto;
// A shipment plan prepared after an order is allocated to a warehouse.
message ShipmentPlan {
// The order for which the shipment is planned.
string orderId = 1;
// The carrier service selected to transport the shipment.
// Max length: 80
string carrierService = 2;
// The time at which the shipment is scheduled to leave the warehouse.
string dispatchAt = 3;
// The monetary value declared for carriage.
// Precision: 12
// Scale: 2
string declaredValue = 4;
// Handling instructions that apply to the shipment.
repeated string handlingTags = 5;
}
Generated output: shipment-plan.sql
CREATE TABLE "ShipmentPlan" (
"orderId" UUID,
"carrierService" VARCHAR(80),
"dispatchAt" TIMESTAMP,
"declaredValue" NUMERIC(18,6),
"handlingTags" JSONB,
PRIMARY KEY ("orderId", "carrierService", "dispatchAt", "declaredValue", "handlingTags")
);
COMMENT ON TABLE "ShipmentPlan" IS 'A shipment plan prepared after an order is allocated to a warehouse.';
COMMENT ON COLUMN "ShipmentPlan"."orderId" IS '{"doc": "The order for which the shipment is planned."}';
COMMENT ON COLUMN "ShipmentPlan"."carrierService" IS '{"doc": "The carrier service selected to transport the shipment."}';
COMMENT ON COLUMN "ShipmentPlan"."dispatchAt" IS '{"doc": "The time at which the shipment is scheduled to leave the warehouse."}';
COMMENT ON COLUMN "ShipmentPlan"."declaredValue" IS '{"doc": "The monetary value declared for carriage."}';
COMMENT ON COLUMN "ShipmentPlan"."handlingTags" IS '{"doc": "Handling instructions that apply to the shipment.", "schema": {"type": "set", "description": "Handling instructions that apply to the shipment.", "items": {"type": "string", "description": "A handling instruction assigned to the shipment."}}}';
Descriptions travel according to each target’s documentation model. Markdown
renders the root and property descriptions as prose. C# turns them into XML
documentation comments, Proto turns them into source comments, and PostgreSQL
stores them in table and column comments. The nested items.description does
not appear in the Markdown, C#, or Proto projection because none emits a
separate artifact for the string item. The SQL projection retains it inside
the JSON schema metadata attached to handlingTags. That difference is
another visible part of the projection policy.
Whether generated files belong in source control is a repository policy. The authority rule stays the same either way. If they are committed for packaging, review, or downstream tools, a clean regeneration can detect manual edits. If they are omitted, the build must recreate them before use.
Version pinning matters because a projection includes the behavior of a
specific generator implementation as well as schema content. An upgrade can
change naming, type selection, or layout without a contract change. Review
that movement as a tooling change,
separate from a change to ShipmentPlan.
Change the model once
Suppose fulfillment adds an optional warehouseNote. Add it to
properties, leave it out of required, and regenerate. Documentation then
shows the field as optional, code receives the target language’s optional
shape, and storage or protocol projections apply their corresponding policy.
Do not begin by adding a nullable column, then a nullable C# property, then a Proto field, and finally trying to reconstruct the schema from the debris. That workflow asks every editor to repeat the same decision and creates several places where the answer can differ.
One source contract does not imply that every target has identical syntax or capabilities. It means every target starts from the same declared intent, and every loss in translation has one place to be noticed. Keep the JSON Structure schema deliberate. Make the artifacts cheap.