Skip to main content

A deterministic JSON payload mapper that transforms source data into target structures using dotted paths, indices and wildcards.

Project description

✨ jsonshift

A lightweight Python package to convert one JSON payload into another using a declarative mapping spec defined in JSON.

Designed for deterministic system integrations, data pipelines, and API adapters.


⚙️ Engine rules

  • If the source path does not exist → raises MappingMissingError (unless optional: true is set)

  • If the source value is null / None → the destination receives None (defaults do NOT override None)

  • defaults only fill values when the destination field is absent (never overwrite existing values or None)

  • Supports:

    • dotted paths
    • indexed paths ([0])
    • wildcard paths ([*])
    • automatic list creation
    • infinite nesting depth
  • Supports optional mappings using optional: true

  • Supports dynamic defaults:

    • { "$now": "date" }
    • { "$now": "datetime" }
    • { "$now": "time" }
    • { "$concat": [...] }
    • { "$upper": ... }
    • { "$lower": ... }
    • { "$capitalize": ... }
    • { "$title": ... }
    • { "$format": { "template": "...", "args": {...} } }

🧩 Installation

pip install jsonshift
# or for development:
pip install -e .[dev]

🚀 Complex example (Python)

from jsonshift import Mapper

payload = {
    "customer_name": "John Doe",
    "cpf": "12345678901",
    "email": "john@doe.com",
    "amount": 1500.0,
    "installments": 6,
    "products": [
        {"id": "P-001", "name": "Notebook", "price": 4500.0},
        {"id": "P-002", "name": "Mouse", "price": 250.0}
    ]
}

spec = {
    "map": {
        "customer.name": "customer_name",
        "customer.cpf": "cpf",
        "customer.email": {
            "path": "email",
            "optional": True
        },

        "contract.amount": "amount",
        "contract.installments": "installments",

        "contract.products[*].code": "products[*].id",
        "contract.products[*].title": "products[*].name",
        "contract.products[*].price": "products[*].price",

        "contract.main_product[0].code": "products[0].id",
        "contract.main_product[0].title": "products[0].name"
    },

    "defaults": {
        "contract.type": "CCB",
        "contract.origin": "ORQ",

        "contract.created_date": {"$now": "date"},
        "contract.created_at": {"$now": "datetime"},
        "contract.created_time": {"$now": "time"},

        "contract.products[*].currency": "BRL"
    }
}

out = Mapper().transform(spec, payload)
print(out)

Output (example):

{
  "customer": {
    "name": "John Doe",
    "cpf": "12345678901",
    "email": "john@doe.com"
  },
  "contract": {
    "amount": 1500.0,
    "installments": 6,
    "type": "CCB",
    "origin": "ORQ",
    "created_date": "2025-01-08",
    "created_at": "2025-01-08T12:00:00",
    "created_time": "12:00:00",
    "products": [
      {
        "code": "P-001",
        "title": "Notebook",
        "price": 4500.0,
        "currency": "BRL"
      },
      {
        "code": "P-002",
        "title": "Mouse",
        "price": 250.0,
        "currency": "BRL"
      }
    ],
    "main_product": [
      {
        "code": "P-001",
        "title": "Notebook"
      }
    ]
  }
}

🧠 Dynamic string defaults

In addition to static values and $now, jsonshift supports dynamic string functions inside defaults.

These functions allow composing and transforming values from literals and payload fields in a deterministic and explicit way.


🔹 $concat

Concatenates string literals and payload values.

{
  "defaults": {
    "user.code": {
      "$concat": [
        "USR-",
        { "$path": "id" }
      ]
    }
  }
}

🔹 $upper / $lower

Transforms strings to upper or lower case.

{
  "defaults": {
    "name_upper": {
      "$upper": { "$path": "name" }
    },
    "email_lower": {
      "$lower": { "$path": "email" }
    }
  }
}

🔹 $capitalize

Capitalizes the first character of the string.

{
  "defaults": {
    "first_name": {
      "$capitalize": { "$path": "name" }
    }
  }
}

🔹 $title

Converts the string to title case (first letter of each word).

{
  "defaults": {
    "full_name": {
      "$title": { "$path": "name" }
    }
  }
}

🔹 $format

Formats strings using Python-style templates.

{
  "defaults": {
    "external_id": {
      "$format": {
        "template": "{id}-{cpf}",
        "args": {
          "id": { "$path": "id" },
          "cpf": { "$path": "cpf" }
        }
      }
    }
  }
}

📌 Notes

  • Dynamic functions are evaluated only inside defaults
  • Paths must be explicitly declared using { "$path": "..." }
  • Missing paths raise MappingMissingError
  • If any resolved value is None, the result is None
  • Dynamic defaults never override existing values or None

🖥️ Command-line interface (CLI)

Using the same example from examples/:

jsonshift --spec examples/spec.json --input examples/payload.json

Or via stdin:

cat examples/payload.json | jsonshift --spec examples/spec.json

🧪 Testing

pytest -v

📄 License

MIT © 2025 Pedro Marques

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsonshift-1.3.0.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jsonshift-1.3.0-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file jsonshift-1.3.0.tar.gz.

File metadata

  • Download URL: jsonshift-1.3.0.tar.gz
  • Upload date:
  • Size: 10.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for jsonshift-1.3.0.tar.gz
Algorithm Hash digest
SHA256 25e63a73d093ad213a38a9a24cad7bd66c603686c8b819d0cf42220e1d2c62fc
MD5 7fd6e3b72f2788f4e96a52e2a0a046b5
BLAKE2b-256 51f267ceb8306659e96ad191a62fa954262a12b57233d2bee3d3943ea853ac0f

See more details on using hashes here.

File details

Details for the file jsonshift-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: jsonshift-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for jsonshift-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4de8a485db8d9c4096e0f817c01688de0b786fc70f3fa6cef47c306bf92b9f28
MD5 a4a3091b6f4fd7653b238bab92697181
BLAKE2b-256 85b0bbaf89b084e02815f0382319c4a3c1f529a157eb7200408ca12c8c9503ea

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page