jsontoschema

@maisondigital/jsontoschema

Convert JSON to a simplified, human-readable schema. Programmatically, from the CLI, or as a library in your toolchain.

convert()

Pass a parsed JSON value and get back a schema string. Objects become typed structures, arrays merge into one representative item, and primitives become their type name.

Code
import { convert } from "@maisondigital/jsontoschema";

const schema = convert({
  id: 1,
  name: "Alice",
  tags: ["admin", "user"],
  address: {
    city: "New York",
    zip: "10001",
  },
});
Output
// Output:
{
  id: number,
  name: string,
  tags: string[],
  address: {
    city: string,
    zip: string
  }
}

convertFromString()

Takes a raw JSON string, parses it, and returns either the schema or an error. Safer for user-provided input since it handles parse failures.

Code
import { convertFromString } from "@maisondigital/jsontoschema";

const result = convertFromString(
  '{"id": 1, "name": "Alice"}'
);

if (result.success) {
  console.log(result.schema);
} else {
  console.error(result.error);
}
Return shape
// Result when success:
{
  schema: "{\n  id: number,\n  name: string\n}",
  success: true
}

// Result when error:
{
  error: "Unexpected token ...",
  success: false
}

Include examples

Pass includeExamples: true to show observed values alongside types. String and number literals appear before the type, signaling that they are examples rather than an exhaustive enum.

Code
import { convert } from "@maisondigital/jsontoschema";

convert(
  { brand: "bmw", year: 2024 },
  { includeExamples: true }
);
Output
// Output:
{
  brand: "bmw" | string,
  year: 2024 | number
}

Array merging

When an array contains objects, fields are merged across all items. Fields that appear in some items but not all are marked with ?. Mixed-type fields become unions.

Code
import { convert } from "@maisondigital/jsontoschema";

convert([
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob", role: "admin" },
]);
Output
// Output:
[{
  id: number,
  name: string,
  role?: string
}]

estimateTokens()

Rough token estimation based on character count. Useful for gauging how much of an LLM context window a schema will occupy.

Code
import { estimateTokens } from "@maisondigital/jsontoschema";

// Rough token count (~4 chars per token)
estimateTokens(schemaString);
Output
// Returns a number
// Useful for estimating LLM context usage

estimateTokens("{ id: number }") //  5

CLI

The package includes a CLI that reads JSON from a file or stdin and prints the schema to stdout. No install required with npx.

Terminal
# From a file
npx @maisondigital/jsontoschema data.json

# With examples
npx @maisondigital/jsontoschema data.json --examples

# Custom indent (default: 2)
npx @maisondigital/jsontoschema data.json --indent 4

# From stdin
cat data.json | npx @maisondigital/jsontoschema

# Pipe into a file
curl -s https://api.example.com/users \
  | npx @maisondigital/jsontoschema > schema.txt

Options

OptionTypeDefaultDescription
includeExamplesbooleanfalseShow observed values alongside types
indentnumber2Spaces per indentation level

How it works

Primitive values become their type: string, number, boolean, null.

Objects keep their keys, values become types.

Arrays of primitives become type[].

Arrays of objects merge all items into one representative shape.

Fields missing from some array items become optional (key?).

Mixed types become unions: string | number.

No depth limit. No recursive deduplication.

Try it in the browser

Same conversion engine, visual interface. Paste JSON and see the schema instantly.