Fork me on GitHub
✦ Powered by RFC 9535 JSONPath

Reshape JSON with a template, not code

ajsont maps any JSON payload to the exact shape you need. Describe your output, point at source values with JSONPath, and skip the brittle hand-written mapping functions.

$ npm install @westonfleming/ajsont

The spec is the output shape

Feed in a source payload and a template. Every key in the template becomes a key in the result.

source.json
{
  "person": {
    "firstName": "Jane",
    "lastName": "Doe"
  },
  "contact": { "email": "jane@example.com" },
  "metadata": { "uid": "u-123" },
  "subscription": { "plan": "enterprise" }
}
spec.json
{
  "user": {
    "fullName": {
      "$concat": ["$.person.firstName", " ", "$.person.lastName"]
    },
    "email": { "$path": "$.contact.email" },
    "id": { "$path": "$.metadata.uid" },
    "region": { "$path": "$.geo.region", "$default": "US" },
    "tier": {
      "$if": { "exists": "$.subscription" },
      "then": "premium", "else": "free"
    }
  }
}
output.json
{
  "user": {
    "fullName": "Jane Doe",
    "email": "jane@example.com",
    "id": "u-123",
    "region": "US",
    "tier": "premium"
  }
}
Run this in the playground →

Everything you need to normalize JSON

A focused operator set built on standard JSONPath — nothing proprietary to memorize.

Declarative templates

Your mapping spec mirrors the output. No imperative loops, no manual deep-merging, no boilerplate.

Standard JSONPath

Reference any value with RFC 9535 JSONPath expressions via the battle-tested jsonpath-plus.

Built-in operators

$concat, $coalesce, $if, $lower/$upper/$trim and more for common reshaping.

Missing-value control

Choose omit, null, or error globally or per-field, plus $default fallbacks.

Validate before you run

validateSpec() catches malformed specs up-front and points to exactly where the problem is.

TypeScript & tiny

Full type definitions, dual ESM/CJS, zero config. One dependency, ~10 kB gzipped in the browser.

How it works

Literals pass through. Operators resolve.

ajsont walks your spec recursively. Plain strings, numbers, booleans and null are copied as-is. Any object with a $-prefixed key is treated as an operator node and resolved against the source. That's the whole mental model.

Need a literal key that starts with $? Wrap it in $literal and it's left untouched.

Explore all operators →
transform.ts
import { transform } from "@westonfleming/ajsont";

const result = transform(source, {
  // literal — copied as-is
  schemaVersion: 2,
  // operator — resolved against source
  email: { $lower: "$.contact.Email" },
  displayName: {
    $coalesce: ["$.user.preferredName", "$.user.firstName"],
    $default: "Anonymous",
  },
}, { onMissing: "null" });

Star it. Fork it. Ship it.

ajsont is MIT-licensed and open source. Contributions, issues, and ideas are always welcome.