Metadata-Version: 2.4
Name: json-mapping-transformer
Version: 0.1.1
Summary: A JSON transformer engine that transforms JSON objects based on configurable mapping rules
Home-page: https://github.com/rayhanhasnat/json-transformer
Author: Rayhan Hasnat
Author-email: r.hasnat@gmail.com
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python
Dynamic: summary

# JSON Transformer

A Python-based JSON transformation engine that can transform JSON objects based on configurable mapping rules.

## Features

- Transform any JSON object to a different structure using mapping configuration
- Support for deep nested JSON objects, arrays, and complex hierarchies
- Handle arrays, objects within arrays, and arrays within objects
- Concatenate fields from different levels in the source JSON
- Exclude missing fields from the output
- Easy-to-understand mapping syntax
- Command-line interface for quick transformations

## Installation

1. Clone the repository:
```bash
git clone https://github.com/rayhanhasnat/json-transformer.git
cd json-transformer
```

2. Install the package:
```bash
pip install -e .
```

Or install directly from PyPI:
```bash
pip install json-mapping-transformer
```

## Usage

### Python API

#### Basic Usage

```python
from json_transformer import transform_json

# Source JSON
source = {
    "name": "John Doe",
    "age": 30,
    "email": "john.doe@example.com"
}

# Mapping configuration
mapping = {
    "full_name": "name",
    "user_age": "age"
}

# Transform the JSON
result = transform_json(source, mapping)
print(result)
# Output: {"full_name": "John Doe", "user_age": 30}
```

#### Advanced Usage

##### Nested Object Mapping

```python
source = {
    "user": {
        "personal": {
            "name": "John Doe",
            "age": 30
        },
        "contact": {
            "email": "john.doe@example.com",
            "phone": "555-1234"
        }
    },
    "company": "Acme Inc."
}

mapping = {
    "name": "user.personal.name",
    "contact_info": {
        "email": "user.contact.email",
        "phone": "user.contact.phone"
    },
    "employer": "company"
}

result = transform_json(source, mapping)
# Output:
# {
#     "name": "John Doe",
#     "contact_info": {
#         "email": "john.doe@example.com",
#         "phone": "555-1234"
#     },
#     "employer": "Acme Inc."
# }
```

If a nested object mapping (like `contact_info` above) results in an empty object (e.g., if both `user.contact.email` and `user.contact.phone` were missing or `null` in the source), the key for that empty object (`contact_info`) will be omitted from the output.

##### Array Mapping

```python
source = {
    "user": "John Doe",
    "items": [
        {"id": 1, "name": "Item 1", "price": 10.99},
        {"id": 2, "name": "Item 2", "price": 20.50},
        {"id": 3, "name": "Item 3", "price": 5.75}
    ]
}

mapping = {
    "customer": "user",
    "products": {
        "_map": "items",
        "_src": {
            "product_id": "id",
            "product_name": "name",
            "cost": "price"
        }
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "customer": "John Doe",
#     "products": [
#         {"product_id": 1, "product_name": "Item 1", "cost": 10.99},
#         {"product_id": 2, "product_name": "Item 2", "cost": 20.50},
#         {"product_id": 3, "product_name": "Item 3", "cost": 5.75}
#     ]
# }
```

When mapping to an array of objects (like `products`): 
1. Each item in the source array is transformed according to its `_src` mapping.
2. If any transformed item results in an empty object (`{}`), it is removed from the list.
3. If, after this filtering, the entire list of objects becomes empty (or if the source array was initially empty, or all items mapped to `null` primitives that were filtered out), the target key for the array (`products`) will be omitted from the output.

##### Array of Objects to Array of Primitives

Map an array of objects to an array of primitive values (e.g., strings, numbers) by specifying the field to extract from each object. If the extracted value from an object is `null` (or `None`), it will be excluded from the resulting array. If, after processing all objects and excluding `null` values, the resulting array of primitives is empty, the entire target key for this array (e.g., `contact_numbers`) will be omitted from the output.

```python
source = {
    "name": "John Doe",
    "contacts": [
        {"type": "phone", "text": "111111"},
        {"type": "email", "text": None},
        {"type": "mobile", "text": "222222"},
        {"type": "emergency", "text": "333333"}
    ]
}

mapping = {
    "name": "name",
    "contact_numbers": {
        "_map": "contacts",
        "_src": "text"
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "name": "John Doe",
#     "contact_numbers": ["111111", "222222", "333333"]
# }
```

##### Value Collection

Collect values from multiple sources into a single array. The `_args` parameter can include:
- Direct paths to values (strings)
- Array sources with field extraction (objects with `_map` configuration)

```python
source = {
    "user": {
        "name": "John Doe",
        "contacts": [
            {"type": "phone", "number": "111111"},
            {"type": "email", "address": "john@example.com"}
        ]
    },
    "company": {
        "name": "Acme Inc",
        "departments": [
            {"name": "IT", "code": "D001"},
            {"name": "HR", "code": "D002"}
        ]
    }
}

mapping = {
    "collected_info": {
        "_type": "collect",
        "_args": [
            "user.name",
            {
                "_map": "user.contacts",
                "_src": "number"
            },
            {
                "_map": "company.departments",
                "_src": "code"
            }
        ]
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "collected_info": ["John Doe", "111111", "D001", "D002"]
# }
```

##### Field Concatenation

Concatenate multiple fields from the source JSON into a single field in the output. You can use both field paths and literal values in the concatenation.

```python
source = {
    "user": {
        "firstName": "John",
        "lastName": "Doe",
        "title": "Mr"
    },
    "address": {
        "street": "123 Main St",
        "city": "Boston",
        "state": "MA",
        "zip": "02108"
    }
}

mapping = {
    "full_name": {
        "_type": "concat",
        "_args": ["user.title", " ", "user.firstName", " ", "user.lastName"]
    },
    "full_address": {
        "_type": "concat",
        "_args": [
            "address.street",
            ", ",
            "address.city",
            ", ",
            "address.state",
            ", ",
            "address.zip"
        ]
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "full_name": "Mr John Doe",
#     "full_address": "123 Main St, Boston, MA, 02108"
# }
```

The concatenation transformation supports:
- Field paths (e.g., "user.firstName")
- Literal values (e.g., " ")
- Custom separators between values
- Automatic skipping of null or empty values

##### First Available Value

Get the first available (non-null) value from multiple source fields. If none of the source fields have a value, the target field will be excluded from the output. This transformation can be used both at the root level and within array mappings.

###### Basic Usage

```python
source = {
    "code": {
        "coding": [
            {
                "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
                "code": "7980"
            }
        ],
        "text": "Penicillin Allergy"
    }
}

mapping = {
    "code": {
        "_type": "first",
        "_args": [
            "code.coding[0].display",  # This doesn't exist
            "code.text"                # This exists and will be used
        ]
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "code": "Penicillin Allergy"
# }
```

###### Using First Available Value in Arrays

You can use the first available value transformation within array mappings to extract the first non-null value for each array item.

```python
source = {
    "manifestation": [
        {
            "coding": [
                {
                    "system": "http://snomed.info/sct",
                    "code": "39579001",
                    "display": "Anaphylaxis"
                }
            ],
            "text": "Anaphylactic shock"
        },
        {
            "coding": [
                {
                    "system": "http://snomed.info/sct",
                    "code": "247472004",
                    "display": "Hives"
                }
            ]
        }
    ]
}

mapping = {
    "manifestation": {
        "_map": "manifestation",
        "_src": {
            "_type": "first",
            "_args": ["text", "coding[0].display"]
        }
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "manifestation": ["Anaphylactic shock", "Hives"]
# }
```

In this example:
1. For the first array item, it uses "text" ("Anaphylactic shock") since it exists
2. For the second array item, since "text" doesn't exist, it falls back to "coding[0].display" ("Hives")
3. If neither value exists for an item, that item will be excluded from the output array

##### Conditional Mapping

Map values based on conditions. The condition can use various operators to compare values.

```python
source = {
    "age": 25,
    "status": "active"
}

mapping = {
    "age_category": {
        "_type": "conditional",
        "_args": {
            "condition": "age > 18",
            "true": "adult",
            "false": "minor"
        }
    },
    "access_level": {
        "_type": "conditional",
        "_args": {
            "condition": "status == active",
            "true": "full",
            "false": "restricted"
        }
    }
}

result = transform_json(source, mapping)
# Output:
# {
#     "age_category": "adult",
#     "access_level": "full"
# }
```

## Command Line Interface

The package includes a command-line interface for transforming JSON files:

```bash
# Basic usage
json-transform source.json mapping.json output.json

# Pretty print the output
json-transform source.json mapping.json output.json --pretty

# Show verbose output
json-transform source.json mapping.json output.json --verbose

# Get help
json-transform --help
```

### CLI Options

- `--pretty`: Pretty print the output JSON
- `--verbose`: Show detailed transformation information
- `--help`: Show help message

## Special Fields

- `_map`: Specifies array mapping
- `_src`: Defines source mapping for arrays
- `_type`: Specifies transformation type
- `_args`: Provides arguments for transformations

## Transformation Types

1. `concat`: Concatenate multiple values
2. `collect`: Collect values from arrays
3. `first`: Get first available value
4. `conditional`: Map based on conditions

## Condition Operators

- `>`: Greater than
- `>=`: Greater than or equal
- `<`: Less than
- `<=`: Less than or equal
- `==`: Equal to
- `!=`: Not equal to

## Error Handling

- Missing fields return `null`
- Invalid paths are ignored
- Invalid transformations return `null`
- Array index out of bounds returns `null`

## Complex Example

```json
{
    "profile": {
        "name": {
            "_type": "concat",
            "_args": ["user.first_name", " ", "user.last_name"]
        },
        "contact": {
            "email": "user.email",
            "phone": "user.phone"
        },
        "address": {
            "_type": "concat",
            "_args": [
                "user.address.street",
                ", ",
                "user.address.city",
                ", ",
                "user.address.state"
            ]
        }
    },
    "orders": {
        "_map": "user.orders",
        "_src": {
            "order_id": "id",
            "items": {
                "_map": "items",
                "_src": {
                    "name": "product.name",
                    "quantity": "quantity"
                }
            }
        }
    }
}
```

## Contributing

1. Fork the repository
2. Create a feature branch
3. Commit your changes
4. Push to the branch
5. Create a Pull Request

## License

This project is licensed under the MIT License - see the LICENSE file for details. 
