json2md
json2md is a Rust library that validates JSON and renders it as Markdown.
Keep a document's content separate from its layout, and check its fields and
values before producing the document.
From JSON to Markdown
This example creates a release note with a title, summary, and list of changes.
1. Start with the data
The JSON input holds the note's content:
These values describe the content. The headings and list formatting belong in the template introduced below.
2. Define the accepted data
JSON Schema describes which fields and values are accepted. The note's schema requires a title and summary as strings, and changes as an array of strings:
Declaring a property in properties describes its value. Listing it in
required makes its presence mandatory. additionalProperties: false rejects
fields that the schema does not declare.
3. Describe the presentation
A template combines fixed Markdown with values from the JSON input.
Templates use MiniJinja syntax: {{ ... }} inserts a value, and {% ... %}
controls a loop or condition. JSON object properties become template variables.
# {{ title }}
{{ summary }}
## Changes
{% for change in changes %}
- {{ change }}{% endfor %}
Use the Rust library
The library requires Rust 1.98 or later. Add these dependencies to your Cargo.toml:
[]
= "0.1"
= "1.0"
See the API reference for public types and methods.
Compile the view and render
A View compiles one schema and one template. Its render method validates
the input before executing the template, then returns the complete Markdown string.
The complete runnable example embeds the release-note files:
use View;
use Value;
Run it from the repository root:
Standard output matches the expected Markdown, including its final newline:
JSON data becomes a checked Markdown document.
- -
Reuse the view
Keep the View to render more notes without compiling the schema and template again.
Each input is still validated. The view owns its compiled assets, so the original
schema and template can be dropped.
Handle invalid input
Input that fails the schema returns Error::Validation with field paths and diagnostics.
Using the same view, replace the title with a number:
use Error;
use json;
let invalid = json!;
match view.render
The diagnostic for this input is:
/title: 42 is not of type "string"
The /title path identifies the rejected property. No template execution occurs
for this input. Match typed error variants to handle failures; diagnostic wording
and issue ordering can change with dependencies.
Use view.validate(&input) when you only need validation. Rendering always
validates; there is no unchecked rendering method.
Handle optional fields
The note currently requires summary. Remove it from the schema's required
array to make it optional.
The original template's {{ summary }} then fails with Error::Render when that
property is absent. Guard the optional section explicitly:
{% if summary is defined %}
{{ summary }}
{% endif %}
Some MiniJinja filters and tests handle missing values without an error.
Keep a property required in the schema when every document must supply it.
A present JSON null differs from an absent property; this note's string schema
rejects null for summary.
View::new checks the schema and template separately. It does not prove that
the template can render every input accepted by the schema.
Rendering behavior
The renderer preserves Markdown and HTML characters without automatic escaping.
For example, a JSON string containing **bold** produces **bold** in the output.
Choose how to escape or sanitize values for your Markdown renderer.
The final template newline is preserved.
Library callers load files and write output. Schema references within the supplied document are supported; external schema retrieval and template file loading are disabled. Known schema formats are validated. Compile-time schema-template compatibility checking is outside v0.1. See the architecture contract for schema drafts, numeric limits, errors, and trust boundaries.
Render files with the CLI
The command-line tool uses the same library to render files.
Install the json2md-cli package from crates.io. Its binary is named json2md:
Supply your JSON, schema, and template files. The schema and template are required:
Markdown goes to standard output. Use --output PATH to write a file instead:
Run the same release-note example from the repository root without installing:
Every render validates its input. Failures produce diagnostics on standard error and a nonzero exit status. Validation or rendering failures produce no Markdown. An unsuccessful operation preserves an existing output file. Output cannot replace an input, schema, or template file. See the CLI contract for output and diagnostic details.
Development
Repository commands run from the workspace root. To install the CLI from source,
use cargo install --locked --path crates/json2md-cli.
The library integration tests compare real template files against independently written Markdown files. They also exercise invalid schemas, invalid data, strict missing values, schema references, formats, and view reuse. The CLI integration tests invoke the binary with real files. They check exact output, diagnostics, failure status, and preservation of inputs and existing output.
Generate API documentation with cargo doc --locked --workspace --no-deps.
The CI workflow also compares library examples and CLI
output with committed Markdown files.
The GitLab fixtures also exercise finding and
summary bodies as offline regression tests. They are excluded from release packages.
See agent skills for repository authoring guidance and
README guidance for documentation examples.
See publishing for package verification and release commands.
License
Licensed under MIT OR Apache-2.0, at your choice. See LICENSE for both texts.