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
🧩 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,
"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": "email",
"contract.products[*].code": "products[*].id",
"contract.products[*].price": "products[*].price"
},
"defaults": {
"contract.created_at": {"$now": "datetime"},
"contract.currency": "BRL"
}
}
out = Mapper().transform(spec, payload)
print(out)
🧠 Dynamic defaults
Dynamic expressions are supported only inside defaults and are resolved recursively.
All dynamic operators:
- are explicit
- are deterministic
- do not override existing values
- return
Noneif any dependency resolves toNone
🔹 $path
Explicitly resolves a value from the payload.
{
"defaults": {
"user_id": { "$path": "id" }
}
}
🔹 $now
Resolves the current time.
{ "$now": "datetime" }
{ "$now": "date" }
{ "$now": "time" }
{ "$now": "year" }
{ "$now": "month" }
{ "$now": "day" }
🔹 $concat
Concatenates strings and resolved values.
{
"defaults": {
"code": {
"$concat": [
"USR-",
{ "$path": "id" }
]
}
}
}
🔹 String transforms
{ "$upper": { "$path": "name" } }
{ "$lower": { "$path": "email" } }
{ "$capitalize": { "$path": "first_name" } }
{ "$title": { "$path": "full_name" } }
🔢 Math operators
All math operators:
- accept
int,float, or numericstring - use
Decimalinternally - return
float
$add, $sub, $mul, $div, $pow
{
"$mul": {
"value": 100,
"by": 0.92
}
}
Division by zero raises an error.
📅 Date arithmetic with $add
$add also supports date and datetime arithmetic.
{
"$add": {
"value": { "$now": "date" },
"by": { "days": 5 }
}
}
Supported units:
yearsmonthsdayshoursminutesseconds
🔢 $round
Rounds numeric values.
{
"$round": {
"value": 3.14159,
"ndigits": 2
}
}
Works with composed expressions.
🎨 $format
Date formatting
{
"$format": {
"value": "2024-06-01",
"date": {
"parse": "%Y-%m-%d",
"strftime": "%d/%m/%Y"
}
}
}
Masks (CPF / CNPJ / custom)
{
"$format": {
"value": "12345678901",
"mask": "###.###.###-##"
}
}
🔢 Number formatting
{
"$format": {
"value": 10000,
"number": {
"decimals": 2,
"thousand": ".",
"decimal": ","
}
}
}
🔗 Composition
Operators can be nested freely.
{
"$round": {
"value": {
"$mul": {
"value": 0.920066,
"by": 100
}
},
"ndigits": 2
}
}
Result:
92.01
📌 Notes
- Dynamic expressions are evaluated only inside
defaults $pathmust be explicit- Missing paths raise
MappingMissingError - If any resolved value is
None, the result isNone - Defaults never override existing values
🖥️ Command-line interface (CLI)
jsonshift --spec examples/spec.json --input examples/payload.json
Or via stdin:
cat payload.json | jsonshift --spec 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-3.1.0.tar.gz.
File metadata
- Download URL: jsonshift-3.1.0.tar.gz
- Upload date:
- Size: 22.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aafa8b79221da4c0020f826d1965c54cc1f169e3d89d0e6d664080e898b1ac2d
|
|
| MD5 |
1ddb745001d86def1f0049855f979d8c
|
|
| BLAKE2b-256 |
b1dd7805ce4059fefbd9343d038ffe9aee276795e7a2bf78d70ec619c28ddf67
|
File details
Details for the file jsonshift-3.1.0-py3-none-any.whl.
File metadata
- Download URL: jsonshift-3.1.0-py3-none-any.whl
- Upload date:
- Size: 9.2 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 |
edfe323f77ac7b8bcbb7f30022b6cdb4dd6b23058205b36dffd6d9d827f3f28d
|
|
| MD5 |
0f87754a1f604adfb021d92b15ff9d49
|
|
| BLAKE2b-256 |
950b5d41b4ef1770f1fd67a37b076081846a0587522ac3a1a5af7c04be04dccc
|