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.4.0.tar.gz (10.3 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.4.0-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

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

Hashes for jsonshift-1.4.0.tar.gz
Algorithm Hash digest
SHA256 efe2c4d6e18492210768f6f724979bb7db882276409d86ab586587aec052290c
MD5 6eb1165473161ec1a3b589703bfe3624
BLAKE2b-256 12aa3409c79b99613c279bc49c5535b98443b177592580aa57f78c86079ef93f

See more details on using hashes here.

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

Hashes for jsonshift-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7c5ae08097b39f165b5b877d652344f734f118e6b28a8d4c9f1b0c906072bca7
MD5 eeee614b6005b71503745714ba244d7b
BLAKE2b-256 608c90bf804c8edec6a94f7dd3386dd7345d1086f14901522759d8b6385fc620

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