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(unlessoptional: trueis set) -
If the source value is
null/None→ the destination receivesNone(defaults do NOT overrideNone) -
defaultsonly fill values when the destination field is absent (never overwrite existing values orNone) -
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 isNone - 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jsonshift-1.4.0.tar.gz.
File metadata
- Download URL: jsonshift-1.4.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
efe2c4d6e18492210768f6f724979bb7db882276409d86ab586587aec052290c
|
|
| MD5 |
6eb1165473161ec1a3b589703bfe3624
|
|
| BLAKE2b-256 |
12aa3409c79b99613c279bc49c5535b98443b177592580aa57f78c86079ef93f
|
File details
Details for the file jsonshift-1.4.0-py3-none-any.whl.
File metadata
- Download URL: jsonshift-1.4.0-py3-none-any.whl
- Upload date:
- Size: 7.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c5ae08097b39f165b5b877d652344f734f118e6b28a8d4c9f1b0c906072bca7
|
|
| MD5 |
eeee614b6005b71503745714ba244d7b
|
|
| BLAKE2b-256 |
608c90bf804c8edec6a94f7dd3386dd7345d1086f14901522759d8b6385fc620
|