Specification scope: Core with the Alternate Names, Units, and Validation companion specifications.

Tool: Structurize is the JSON Structure-focused command-line interface and conversion toolkit from the Avrotize project.

Generate reference documentation from the schema. When people maintain a property table beside the schema, a field can change while the table continues to describe the old contract. Both files still look valid, so review alone may not expose the difference.

A hand-maintained page can omit declarations that remain present in the schema. JSON Structure keeps names, descriptions, required properties, choices, constraints, units, alternate names, defaults, and examples with the types they qualify.

Structurize’s s2md converter reads those declarations and renders its supported subset as Markdown. In the outputs tested below, it preserves reader-facing descriptions and type details, transforms the schema graph into property lists and sections, and omits the root name and $root. It cannot derive workflow rationale from type declarations. Regenerate the reference with every contract change, and write operational guidance around it rather than into it.

Start with the fulfillment contract

Consider a fulfillment service that receives orders from several sales channels. The abbreviated schema below defines the root object, a reusable address, a choice of delivery methods, and a weight with a unit annotation:

{
  "$schema": "https://json-structure.org/meta/validation/v0/#",
  "$id": "https://schemas.example.com/fulfillment-order",
  "name": "FulfillmentOrderSchema",
  "$uses": ["JSONStructureAlternateNames", "JSONStructureUnits", "JSONStructureValidation"],
  "$root": "#/definitions/FulfillmentOrder",
  "definitions": {
    "Address": {
      "name": "Address",
      "description": "A postal delivery address.",
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "postalCode": {
          "type": "string",
          "altnames": { "warehouseCsv": "postal_code" }
        }
      },
      "required": ["street", "city", "postalCode"]
    },
    "Delivery": {
      "name": "Delivery",
      "type": "choice",
      "choices": {
        "ship": { "type": { "$ref": "#/definitions/Address" } },
        "collectAt": { "type": "string" }
      }
    },
    "FulfillmentOrder": {
      "name": "FulfillmentOrder",
      "description": "An order released to fulfillment.",
      "type": "object",
      "properties": {
        "orderId": { "type": "uuid" },
        "delivery": {
          "type": { "$ref": "#/definitions/Delivery" }
        },
        "packageWeight": {
          "type": "double",
          "minimum": 0,
          "unit": "kg",
          "default": 0,
          "examples": [1.25]
        }
      },
      "required": ["orderId", "delivery"]
    }
  }
}

The schema is intentionally richer than a page of prose. Names, descriptions, references, constraints, units, alternate names, defaults, and examples sit next to the declarations they qualify. A reviewer can discuss one change in one place.

Keeping the type and unit together lets reviewers see changes that a property table can miss. If packageWeight changes from kilograms to grams, the type can remain double while the scale changes from one kilogram to one gram, a factor of 1,000. A hand-maintained table that records only double remains syntactically correct but no longer states the same unit. The schema records the type and unit together.

Render the page with s2md

s2md takes an input schema and an output path:

New-Item -ItemType Directory -Force generated | Out-Null
structurize s2md fulfillment-order.struct.json --out generated/fulfillment-order.md

The s2md command registration connects that command to the Markdown converter. The structuretomd.py implementation emits a # schema heading, the schema ID and extension list, then a ## Definitions section. It renders the Address, Delivery, and FulfillmentOrder descriptions and property or choice lists. The weight entry contains unit: kg, examples: [1.25], default: 0, and minimum: 0; the postal-code entry contains its alternate name. References appear as literal dictionaries such as {'$ref': '#/definitions/Address'} rather than linked type names. The output omits the root name and $root.

Generated output: fulfillment-order.md
# fulfillment-order.struct
**Schema ID:** `https://schemas.example.com/fulfillment-order`
**Uses Extensions:** JSONStructureAlternateNames, JSONStructureUnits, JSONStructureValidation
## Definitions

### Address

A postal delivery address.
**Properties:**
- **street** (required): `string`
- **city** (required): `string`
- **postalCode** (required): `string`
  - Extensions: altnames: {warehouseCsv: postal_code}

### Delivery

**Choices:**
- **ship**: `{'$ref': '#/definitions/Address'}`
- **collectAt**: `string`

### FulfillmentOrder

An order released to fulfillment.
**Properties:**
- **orderId** (required): `uuid`
- **delivery** (required): `{'$ref': '#/definitions/Delivery'}`
- **packageWeight**: `double`
  - Extensions: unit: kg, examples: [1.25], default: 0
  - Constraints: minimum: 0

The directory component in the output path is intentional. With this release, --out fulfillment-order.md fails on Windows with The system cannot find the path specified: ''; generated/fulfillment-order.md succeeds once generated exists.

Check the generated file into a documentation site if that makes reviews and publishing easier, or build it on demand. Either policy works as long as the build can reproduce the page and detect stale output. For a repository that commits generated Markdown, use this check:

New-Item -ItemType Directory -Force docs/reference | Out-Null
structurize s2md schemas/fulfillment-order.struct.json `
  --out docs/reference/fulfillment-order.md
git diff --exit-code -- docs/reference/fulfillment-order.md

The first command refreshes the projection. The second fails when a schema change arrived without its generated documentation. Reviewers then see both the contract edit and its reader-facing consequence in the same change.

Write operational guidance separately

Generated reference material answers precise questions: Which properties are required? What does this choice contain? Which unit qualifies the number? It does not explain why store collection and postal shipment share one operation, how warehouse allocation works, or what to do when a carrier rejects an address.

Generate declarations, constraints, defaults, and examples from the schema. Write workflows, rationale, failure handling, and operational guidance by hand. Link the authored guidance to the generated reference instead of copying its property tables.

Avoid editing the generated Markdown, even for a tiny correction. Fix a wrong description in the schema. Fix a rendering defect in the converter. Put a workflow explanation in an authored article. The next build will overwrite a local patch to generated output, as it should.

Regenerate in CI

Generation removes transcription drift, but it does not remove review. Inspect the schema first, then inspect the rendered descriptions and examples. In CI, run the exact structurize s2md command used by the repository and follow it with git diff --exit-code for the generated Markdown. A stale reference page then fails the build instead of becoming a second contract.