Metadata-Version: 2.4
Name: jsonshift
Version: 1.3.0
Summary: A deterministic JSON payload mapper that transforms source data into target structures using dotted paths, indices and wildcards.
Author-email: pml-guardian <pml@guardian-asset.com>
License: MIT
Project-URL: Homepage, https://github.com/pml-guardian/jsonshift
Project-URL: Documentation, https://github.com/pml-guardian/jsonshift#readme
Project-URL: Issues, https://github.com/pml-guardian/jsonshift/issues
Keywords: json,mapping,transform,payload,data,mapper,integration,etl,adapter
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8.4.2; extra == "dev"
Dynamic: license-file

# ✨ 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

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

---

## 🚀 Complex example (Python)

```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):

```json
{
  "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.

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

---

### 🔹 `$upper` / `$lower`

Transforms strings to upper or lower case.

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

---

### 🔹 `$capitalize`

Capitalizes the first character of the string.

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

---

### 🔹 `$title`

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

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

---

### 🔹 `$format`

Formats strings using Python-style templates.

```json
{
  "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/`:

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

Or via `stdin`:

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

---

## 🧪 Testing

```bash
pytest -v
```

---

## 📄 License

MIT © 2025 Pedro Marques
