npm package: @naman22khater/json-mapper
Description: A powerful JSON mapping and transformation engine
Latest version: 1.0.2

# @naman22khater/json-mapper

A powerful JSON mapping and transformation engine for converting data from one schema to another. Perfect for ETL pipelines, API integrations, and data migration tasks.

[![npm version](https://badge.fury.io/js/%40naman22khater%2Fjson-mapper.svg)](https://www.npmjs.com/package/@naman22khater/json-mapper)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 🔄 **Schema-based Mapping** - Define source and target schemas with field-level mappings
- 📦 **Array Support** - Handle nested arrays with wildcard (`*`) path notation
- 🔧 **Built-in Transformations** - Convert types, format strings, parse dates, and more
- ✨ **Custom Transformations** - Define your own transformation functions
- ✅ **Validation** - Validate source data against schemas
- 📊 **Batch Processing** - Transform multiple objects in a single call
- 🎯 **TypeScript Support** - Full type definitions included

## Installation

```bash
npm install @naman22khater/json-mapper
```

```bash
yarn add @naman22khater/json-mapper
```

```bash
pnpm add @naman22khater/json-mapper
```

## Quick Start

```typescript
import { JsonMapperEngine, createMapper, MappingConfig } from '@naman22khater/json-mapper';

// Define your mapping configuration
const config: MappingConfig = {
  version: '1.0',
  source: {
    name: 'UserAPI',
    fields: [
      { id: 'src-1', name: 'user_name', type: 'String' },
      { id: 'src-2', name: 'user_email', type: 'String' },
      { id: 'src-3', name: 'is_active', type: 'Boolean' },
    ],
  },
  target: {
    name: 'CRM',
    fields: [
      { id: 'tgt-1', name: 'fullName', type: 'String' },
      { id: 'tgt-2', name: 'emailAddress', type: 'String' },
      { id: 'tgt-3', name: 'status', type: 'String' },
    ],
  },
  mappings: [
    { id: 'map-1', sourceFieldId: 'src-1', targetFieldId: 'tgt-1' },
    { id: 'map-2', sourceFieldId: 'src-2', targetFieldId: 'tgt-2' },
    { 
      id: 'map-3', 
      sourceFieldId: 'src-3', 
      targetFieldId: 'tgt-3',
      transformation: 'toString' 
    },
  ],
};

// Create the mapper
const mapper = createMapper(config);

// Transform data
const result = mapper.transform({
  user_name: 'John Doe',
  user_email: 'john@example.com',
  is_active: true,
});

console.log(result);
// {
//   success: true,
//   data: {
//     fullName: 'John Doe',
//     emailAddress: 'john@example.com',
//     status: 'true'
//   }
// }
```

## Working with Nested Objects

```typescript
const config: MappingConfig = {
  version: '1.0',
  source: {
    name: 'Source',
    fields: [
      {
        id: 'src-addr',
        name: 'address',
        type: 'Object',
        children: [
          { id: 'src-street', name: 'street', type: 'String' },
          { id: 'src-city', name: 'city', type: 'String' },
        ],
      },
    ],
  },
  target: {
    name: 'Target',
    fields: [
      {
        id: 'tgt-location',
        name: 'location',
        type: 'Object',
        children: [
          { id: 'tgt-addr', name: 'streetAddress', type: 'String' },
          { id: 'tgt-city', name: 'cityName', type: 'String' },
        ],
      },
    ],
  },
  mappings: [
    { id: 'map-1', sourceFieldId: 'src-street', targetFieldId: 'tgt-addr' },
    { id: 'map-2', sourceFieldId: 'src-city', targetFieldId: 'tgt-city' },
  ],
};

const mapper = createMapper(config);

const result = mapper.transform({
  address: {
    street: '123 Main St',
    city: 'New York',
  },
});

// Result:
// {
//   location: {
//     streetAddress: '123 Main St',
//     cityName: 'New York'
//   }
// }
```

## Working with Arrays

The engine supports array traversal using the `*` wildcard notation. When you define a field inside an Array type, the path automatically includes `.*` to indicate array element access.

```typescript
const config: MappingConfig = {
  version: '1.0',
  source: {
    name: 'OrdersAPI',
    fields: [
      {
        id: 'src-orders',
        name: 'orders',
        type: 'Array',
        children: [
          { id: 'src-product', name: 'product_name', type: 'String' },
          { id: 'src-qty', name: 'quantity', type: 'Number' },
        ],
      },
    ],
  },
  target: {
    name: 'Inventory',
    fields: [
      {
        id: 'tgt-items',
        name: 'items',
        type: 'Array',
        children: [
          { id: 'tgt-name', name: 'name', type: 'String' },
          { id: 'tgt-count', name: 'count', type: 'Number' },
        ],
      },
    ],
  },
  mappings: [
    // Maps orders.*.product_name → items.*.name
    { id: 'map-1', sourceFieldId: 'src-product', targetFieldId: 'tgt-name' },
    // Maps orders.*.quantity → items.*.count
    { id: 'map-2', sourceFieldId: 'src-qty', targetFieldId: 'tgt-count' },
  ],
};

const mapper = createMapper(config);

const result = mapper.transform({
  orders: [
    { product_name: 'Widget', quantity: 5 },
    { product_name: 'Gadget', quantity: 3 },
  ],
});

// Result:
// {
//   items: [
//     { name: 'Widget', count: 5 },
//     { name: 'Gadget', count: 3 }
//   ]
// }
```

## Transformations

The engine supports several built-in transformations:

| Transformation | Description | Example |
|---------------|-------------|---------|
| `direct` | Copy value as-is (default) | `"hello"` → `"hello"` |
| `toString` | Convert to string | `123` → `"123"` |
| `toNumber` | Convert to number | `"42"` → `42` |
| `toBoolean` | Convert to boolean | `"true"` → `true` |
| `toUpperCase` | Convert string to uppercase | `"hello"` → `"HELLO"` |
| `toLowerCase` | Convert string to lowercase | `"HELLO"` → `"hello"` |
| `trim` | Remove whitespace from string ends | `" hello "` → `"hello"` |
| `dateFormat` | Format date values | `"2024-01-01"` → `"2024-01-01T00:00:00.000Z"` |
| `custom` | Apply custom transformation | User-defined |

### Using Transformations

```typescript
const mappings = [
  {
    id: 'map-1',
    sourceFieldId: 'src-name',
    targetFieldId: 'tgt-name',
    transformation: 'toUpperCase',
  },
  {
    id: 'map-2',
    sourceFieldId: 'src-price',
    targetFieldId: 'tgt-price',
    transformation: 'toNumber',
    transformationOptions: {
      defaultValue: 0, // Used if conversion fails
    },
  },
  {
    id: 'map-3',
    sourceFieldId: 'src-date',
    targetFieldId: 'tgt-date',
    transformation: 'dateFormat',
    transformationOptions: {
      dateFormat: 'ISO',
    },
  },
];
```

### Custom Transformations

```typescript
const mappings = [
  {
    id: 'map-1',
    sourceFieldId: 'src-price',
    targetFieldId: 'tgt-price',
    transformation: 'custom',
    transformationOptions: {
      customFunction: 'value * 1.1', // Add 10% markup
    },
  },
];
```

## API Reference

### `JsonMapperEngine`

The main class for performing transformations.

#### Constructor

```typescript
new JsonMapperEngine(config: MappingConfig)
```

#### Methods

##### `transform(sourceData: Record<string, unknown>): MappingResult`

Transform a single source object to the target format.

```typescript
const result = mapper.transform({ name: 'John' });
// Returns: { success: boolean, data?: object, errors?: MappingError[] }
```

##### `transformBatch(sourceDataArray: Record<string, unknown>[]): MappingResult[]`

Transform an array of source objects.

```typescript
const results = mapper.transformBatch([
  { name: 'John' },
  { name: 'Jane' },
]);
```

##### `validateSource(sourceData: Record<string, unknown>): { valid: boolean, errors: string[] }`

Validate source data against the source schema.

```typescript
const validation = mapper.validateSource({ name: 'John' });
if (!validation.valid) {
  console.log(validation.errors);
}
```

##### `generateSampleOutput(): Record<string, unknown>`

Generate an empty target structure based on the target schema.

```typescript
const sample = mapper.generateSampleOutput();
// Returns target structure with default values
```

##### `getConfig(): MappingConfig`

Get the current mapping configuration.

##### `setConfig(config: MappingConfig): void`

Update the mapping configuration.

### `createMapper(config: MappingConfig): JsonMapperEngine`

Factory function to create a new mapper instance.

```typescript
const mapper = createMapper(config);
```

## Types

### `Field`

```typescript
interface Field {
  id: string;
  name: string;
  type: FieldType; // 'String' | 'Number' | 'Boolean' | 'Object' | 'Array'
  children?: Field[];
  expanded?: boolean;
}
```

### `Mapping`

```typescript
interface Mapping {
  id: string;
  sourceFieldId: string;
  targetFieldId: string;
  transformation?: TransformationType;
  transformationOptions?: TransformationOptions;
}
```

### `MappingConfig`

```typescript
interface MappingConfig {
  version: string;
  source: SourceConfig;
  target: TargetConfig;
  mappings: Mapping[];
}
```

### `MappingResult`

```typescript
interface MappingResult {
  success: boolean;
  data?: Record<string, unknown>;
  errors?: MappingError[];
}
```

### `MappingError`

```typescript
interface MappingError {
  field: string;
  message: string;
  sourceValue?: unknown;
}
```

## File Format (.jmap)

The JSON Mapper uses `.jmap` files for storing mapping configurations. These files are JSON-formatted and contain the complete mapping configuration:

```json
{
  "version": "1.0",
  "exportedAt": "2026-01-04T12:00:00.000Z",
  "source": {
    "name": "SourceSystem",
    "fields": [...]
  },
  "target": {
    "name": "TargetSystem", 
    "fields": [...]
  },
  "mappings": [...]
}
```

## Error Handling

The engine provides detailed error information when transformations fail:

```typescript
const result = mapper.transform(data);

if (!result.success) {
  result.errors?.forEach(error => {
    console.log(`Field: ${error.field}`);
    console.log(`Message: ${error.message}`);
    console.log(`Source Value: ${error.sourceValue}`);
  });
}
```

## License

MIT © Naman Khater

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.