@naman22khater/json-mapper
v1.0.2
Published
A powerful JSON mapping and transformation engine
Maintainers
Readme
@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.
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
npm install @naman22khater/json-mapperyarn add @naman22khater/json-mapperpnpm add @naman22khater/json-mapperQuick Start
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: '[email protected]',
is_active: true,
});
console.log(result);
// {
// success: true,
// data: {
// fullName: 'John Doe',
// emailAddress: '[email protected]',
// status: 'true'
// }
// }Working with Nested Objects
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.
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
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
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
new JsonMapperEngine(config: MappingConfig)Methods
transform(sourceData: Record<string, unknown>): MappingResult
Transform a single source object to the target format.
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.
const results = mapper.transformBatch([
{ name: 'John' },
{ name: 'Jane' },
]);validateSource(sourceData: Record<string, unknown>): { valid: boolean, errors: string[] }
Validate source data against the source schema.
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.
const sample = mapper.generateSampleOutput();
// Returns target structure with default valuesgetConfig(): 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.
const mapper = createMapper(config);Types
Field
interface Field {
id: string;
name: string;
type: FieldType; // 'String' | 'Number' | 'Boolean' | 'Object' | 'Array'
children?: Field[];
expanded?: boolean;
}Mapping
interface Mapping {
id: string;
sourceFieldId: string;
targetFieldId: string;
transformation?: TransformationType;
transformationOptions?: TransformationOptions;
}MappingConfig
interface MappingConfig {
version: string;
source: SourceConfig;
target: TargetConfig;
mappings: Mapping[];
}MappingResult
interface MappingResult {
success: boolean;
data?: Record<string, unknown>;
errors?: MappingError[];
}MappingError
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:
{
"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:
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.
