n8n-nodes-data-mapper 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +250 -2
- package/dist/nodes/DataMapper/DataMapper.node.d.ts +5 -0
- package/dist/nodes/DataMapper/DataMapper.node.js +450 -0
- package/dist/nodes/DataMapper/DataMapper.node.js.map +1 -0
- package/dist/nodes/DataMapper/DataMapper.node.json +14 -0
- package/dist/nodes/DataMapper/mapper.dark.svg +1 -0
- package/dist/nodes/DataMapper/mapper.svg +1 -0
- package/dist/nodes/DataMapper/mapping.d.ts +34 -0
- package/dist/nodes/DataMapper/mapping.js +451 -0
- package/dist/nodes/DataMapper/mapping.js.map +1 -0
- package/dist/nodes/DataMapper/xml.d.ts +38 -0
- package/dist/nodes/DataMapper/xml.js +316 -0
- package/dist/nodes/DataMapper/xml.js.map +1 -0
- package/dist/package.json +62 -0
- package/package.json +60 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SilentJMA
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,251 @@
|
|
|
1
|
-
#
|
|
1
|
+
# n8n-nodes-data-mapper
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/n8n-nodes-data-mapper)
|
|
4
|
+
[](https://github.com/SilentJMA/n8n-nodes-data-mapper/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
An [n8n](https://n8n.io) community node that **maps data from one structure to another**. Rename and nest fields, map whole arrays with `[*]`, convert types, format dates, translate codes with lookup tables, and convert between **JSON and XML**, all in one node.
|
|
8
|
+
|
|
9
|
+
Use it to turn an API response into your internal format, shape database rows into a request payload, read a supplier's XML into clean JSON, or pull just the useful parts out of a large payload.
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick start](#quick-start)
|
|
13
|
+
- [Paths](#paths)
|
|
14
|
+
- [Parameters](#parameters)
|
|
15
|
+
- [Transforms](#transforms)
|
|
16
|
+
- [Reusable JSON definitions](#reusable-json-definitions)
|
|
17
|
+
- [Working with XML](#working-with-xml)
|
|
18
|
+
- [Examples](#examples)
|
|
19
|
+
- [Compatibility](#compatibility)
|
|
20
|
+
- [Development](#development)
|
|
21
|
+
- [License](#license)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
### From the n8n UI (recommended)
|
|
26
|
+
|
|
27
|
+
1. In n8n, open **Settings → Community Nodes**.
|
|
28
|
+
2. Select **Install**, enter `n8n-nodes-data-mapper`, accept the risk notice and install.
|
|
29
|
+
3. Search for **Data Mapper** in the node panel.
|
|
30
|
+
|
|
31
|
+
### Manually (self-hosted, Docker, Unraid)
|
|
32
|
+
|
|
33
|
+
The package has no runtime dependencies, so you can drop it into n8n's `custom` folder:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# inside your n8n data folder (mapped to /home/node/.n8n in the official Docker image)
|
|
37
|
+
mkdir -p custom && cd custom
|
|
38
|
+
npm pack n8n-nodes-data-mapper
|
|
39
|
+
tar -xzf n8n-nodes-data-mapper-*.tgz && mv package n8n-nodes-data-mapper
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Restart n8n afterwards.
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
Given this input item:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"id": "A1",
|
|
51
|
+
"customer": { "name": " Ann ", "country": "NL" },
|
|
52
|
+
"items": [
|
|
53
|
+
{ "sku": "P-1", "qty": "2" },
|
|
54
|
+
{ "sku": "P-2", "qty": "1" }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
add these mappings:
|
|
60
|
+
|
|
61
|
+
| Source Path | Target Path | Transform |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| `id` | `orderId` | |
|
|
64
|
+
| `customer.name` | `buyer.name` | Trim |
|
|
65
|
+
| `customer.country` | `buyer.country` | Lookup Table: `NL=Netherlands` |
|
|
66
|
+
| `items[*].sku` | `lines[*].productCode` | |
|
|
67
|
+
| `items[*].qty` | `lines[*].quantity` | To Integer |
|
|
68
|
+
| *Fixed Value* `EUR` | `lines[*].currency` | |
|
|
69
|
+
|
|
70
|
+
and get:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"orderId": "A1",
|
|
75
|
+
"buyer": { "name": "Ann", "country": "Netherlands" },
|
|
76
|
+
"lines": [
|
|
77
|
+
{ "productCode": "P-1", "quantity": 2, "currency": "EUR" },
|
|
78
|
+
{ "productCode": "P-2", "quantity": 1, "currency": "EUR" }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Import [`examples/data-mapper-examples.json`](examples/data-mapper-examples.json) into n8n (**Workflows → Import from File**) to try this and the XML examples.
|
|
84
|
+
|
|
85
|
+
## Paths
|
|
86
|
+
|
|
87
|
+
| Syntax | Meaning |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `customer.name` | Nested field |
|
|
90
|
+
| `items[0].sku` | One array element |
|
|
91
|
+
| `items[*].sku` | Every array element |
|
|
92
|
+
| `["first name"]` or `data["x-id"]` | Keys with spaces or special characters |
|
|
93
|
+
| *(empty)* | The whole input (source) or the whole output (target, value must be an object) |
|
|
94
|
+
|
|
95
|
+
How `[*]` in the source and target line up:
|
|
96
|
+
|
|
97
|
+
- **Same number of `[*]`**: elements are matched by position. `items[*].sku` → `lines[*].code` writes `lines[0].code`, `lines[1].code`, … Mappings that share a target array build up the same elements, so several mappings can fill in one array of objects.
|
|
98
|
+
- **Fewer `[*]` in the target**: the extra levels are collected into a list. `items[*].sku` → `skus` gives `["P-1", "P-2"]`. `orders[*].items[*].sku` → `orders[*].skus` gives one list per order, and an empty list for an order with no items.
|
|
99
|
+
- **More `[*]` in the target**: not allowed, except for a fixed value, which is written to every existing element (`EUR` → `lines[*].currency`).
|
|
100
|
+
- A single object where `[*]` expects an array is treated as a one-element array. This matters for XML, where one child element is not an array.
|
|
101
|
+
|
|
102
|
+
Mappings run in order, and a later mapping to the same target overwrites an earlier one.
|
|
103
|
+
|
|
104
|
+
## Parameters
|
|
105
|
+
|
|
106
|
+
| Parameter | Description |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| **Input** | *Item JSON* maps each incoming item. *Text Field* first parses JSON or XML text stored in a field. *File* first parses a JSON or XML binary file. |
|
|
109
|
+
| **Input Field** / **Input Binary Field** | Which field holds the text or file. |
|
|
110
|
+
| **Input Format** | *Detect Automatically* (XML when the text starts with `<`), *JSON* or *XML*. |
|
|
111
|
+
| **Define Mappings** | *Using Fields Below* or *Using JSON*. See [Reusable JSON definitions](#reusable-json-definitions). |
|
|
112
|
+
| **Mappings** | The list of mappings. Each has a **Source** (*Input Field* path or *Fixed Value*, which can be an expression), a **Target Path**, an optional **Transform**, and **If Source Is Missing**: *Leave Out* (default), *Set to Null*, *Use Default* or *Stop With Error*. |
|
|
113
|
+
| **Unmapped Fields** | *Drop* outputs only mapped fields. *Keep* starts from a copy of the input and applies the mappings on top. |
|
|
114
|
+
| **Output Format** | *JSON* (the item), *JSON File*, *XML* (text in a field) or *XML File*. |
|
|
115
|
+
| **Put Output in Field** | *XML only.* Default `xml`. |
|
|
116
|
+
| **Put Output File in Field** / **File Name** | *File outputs only.* Defaults `data` and `output.xml` / `output.json`. |
|
|
117
|
+
| **Root Element** | *XML outputs only.* Leave empty to use the single top-level field as the root, or `root` when there are several. |
|
|
118
|
+
| **Include XML Declaration** | *XML outputs only.* Adds `<?xml version="1.0" encoding="UTF-8"?>`. |
|
|
119
|
+
|
|
120
|
+
Turn on **Settings → On Error → Continue** to output failed items as `{ "error": "…" }`. Every error names the mapping that caused it, e.g. `Mapping 3 (→ lines[*].quantity): Cannot convert "two" to a number`.
|
|
121
|
+
|
|
122
|
+
## Transforms
|
|
123
|
+
|
|
124
|
+
| Transform | Options | Example |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| None | | |
|
|
127
|
+
| To String | | `42` → `"42"`; objects become JSON text |
|
|
128
|
+
| To Number | | `"10.5"` → `10.5` (fails on non-numbers) |
|
|
129
|
+
| To Integer | | `"10.9"` → `10` |
|
|
130
|
+
| Round | Decimal Places | `10.456` → `10.46` |
|
|
131
|
+
| To Boolean | | `yes`/`no`, `true`/`false`, `1`/`0`, `on`/`off`, `y`/`n` |
|
|
132
|
+
| Trim, Uppercase, Lowercase | | Also applied to every string in a list |
|
|
133
|
+
| Format Date | Input Date Format, Output Date Format | `20261017` with `yyyyMMdd` → `17/10/2026` with `dd/MM/yyyy`. Empty input format reads ISO 8601 or Unix milliseconds; empty output format writes ISO 8601. Tokens: `yyyy MM dd HH mm ss SSS`, in UTC. |
|
|
134
|
+
| Split Into List | Separator | `"a, b"` → `["a", "b"]` |
|
|
135
|
+
| Join List | Separator | `["a", "b"]` → `"a,b"`. With `items[*].sku` → `skuList` it joins the collected list. |
|
|
136
|
+
| Lookup Table | Lookup Table, If No Match | Table as `key=value` lines or a JSON object. If no match: *Keep Original Value*, *Use Default*, *Set to Null* or *Stop With Error*. |
|
|
137
|
+
|
|
138
|
+
For anything else, use a **Fixed Value** with an expression, e.g. `{{ $json.first + ' ' + $json.last }}`.
|
|
139
|
+
|
|
140
|
+
## Reusable JSON definitions
|
|
141
|
+
|
|
142
|
+
Set **Define Mappings** to **Using JSON** to describe the mapping as data. You can paste it in, or load it with an expression so one mapping is shared by many workflows. For example, `{{ $('Get mapping').item.json.mapping }}` can read it from a database row, a file or a static data node.
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
[
|
|
146
|
+
{ "source": "id", "target": "orderId" },
|
|
147
|
+
{ "source": "customer.name", "target": "buyer.name", "transform": "trim" },
|
|
148
|
+
{ "source": "items[*].sku", "target": "lines[*].productCode" },
|
|
149
|
+
{ "source": "items[*].qty", "target": "lines[*].quantity", "transform": "integer" },
|
|
150
|
+
{ "source": "created", "target": "orderDate", "transform": "date", "dateInputFormat": "yyyyMMdd", "dateOutputFormat": "yyyy-MM-dd" },
|
|
151
|
+
{ "source": "customer.country", "target": "buyer.country", "transform": "lookup", "lookup": { "NL": "Netherlands" }, "lookupFallback": "keep" },
|
|
152
|
+
{ "source": "note", "target": "comment", "ifMissing": "default", "default": "" },
|
|
153
|
+
{ "value": "EUR", "target": "lines[*].currency" }
|
|
154
|
+
]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`{ "mappings": [ … ] }` also works. Available properties per mapping:
|
|
158
|
+
|
|
159
|
+
| Property | Values |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| `target` | Target path (required) |
|
|
162
|
+
| `source` | Source path |
|
|
163
|
+
| `value` | Fixed value, used instead of `source` |
|
|
164
|
+
| `transform` | `none`, `string`, `number`, `integer`, `round`, `boolean`, `trim`, `uppercase`, `lowercase`, `date`, `split`, `join`, `lookup` |
|
|
165
|
+
| `ifMissing` | `skip` (default), `null`, `default`, `error` |
|
|
166
|
+
| `default` | Value for `ifMissing: "default"` |
|
|
167
|
+
| `separator` | For `split` and `join` (default `,`) |
|
|
168
|
+
| `decimals` | For `round` |
|
|
169
|
+
| `dateInputFormat`, `dateOutputFormat` | For `date` |
|
|
170
|
+
| `lookup` | Object or `key=value` lines, for `lookup` |
|
|
171
|
+
| `lookupFallback` | `keep` (default), `default`, `null`, `error` |
|
|
172
|
+
| `lookupDefault` | Value for `lookupFallback: "default"` |
|
|
173
|
+
|
|
174
|
+
Unknown properties are rejected, so a typo such as `"tranform"` fails loudly instead of being ignored.
|
|
175
|
+
|
|
176
|
+
## Working with XML
|
|
177
|
+
|
|
178
|
+
XML input is converted with the same conventions as n8n's XML node: attributes go under `$` and an element's text, when it also has attributes or children, goes under `_`.
|
|
179
|
+
|
|
180
|
+
```xml
|
|
181
|
+
<order ID="7">
|
|
182
|
+
<line><sku>A</sku><qty>3</qty></line>
|
|
183
|
+
<line><sku>B</sku><qty>1</qty></line>
|
|
184
|
+
<total currency="EUR">12.50</total>
|
|
185
|
+
</order>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
| Value | Path |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `7` | `order.$.ID` |
|
|
191
|
+
| every SKU | `order.line[*].sku` |
|
|
192
|
+
| `12.50` | `order.total._` |
|
|
193
|
+
| `EUR` | `order.total.$.currency` |
|
|
194
|
+
|
|
195
|
+
XML output works the other way round. Write to `$` for attributes, `_` for text, and a list becomes repeated elements:
|
|
196
|
+
|
|
197
|
+
| Source Path | Target Path |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `id` | `order.$.id` |
|
|
200
|
+
| `items[*].sku` | `order.line[*].$.sku` |
|
|
201
|
+
| `items[*].qty` | `order.line[*].quantity` |
|
|
202
|
+
|
|
203
|
+
```xml
|
|
204
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
205
|
+
<order id="A1">
|
|
206
|
+
<line sku="P-1">
|
|
207
|
+
<quantity>2</quantity>
|
|
208
|
+
</line>
|
|
209
|
+
<line sku="P-2">
|
|
210
|
+
<quantity>1</quantity>
|
|
211
|
+
</line>
|
|
212
|
+
</order>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Special characters are escaped automatically. Invalid XML input fails with the line and column of the problem.
|
|
216
|
+
|
|
217
|
+
## Examples
|
|
218
|
+
|
|
219
|
+
### Distill a large API response
|
|
220
|
+
|
|
221
|
+
Keep three fields from a big payload: **Unmapped Fields** *Drop*, with mappings `data.user.id` → `id`, `data.user.profile.email` → `email` (Lowercase) and `data.user.roles[*].name` → `roles`.
|
|
222
|
+
|
|
223
|
+
### Convert JSON to an XML file for an SFTP upload
|
|
224
|
+
|
|
225
|
+
**Output Format** *XML File*, **Root Element** `invoice`, **File Name** `{{ $json.number }}.xml`, then connect an FTP node.
|
|
226
|
+
|
|
227
|
+
### Normalize data from several sources
|
|
228
|
+
|
|
229
|
+
Put one Data Mapper on each source branch, each mapping into the same target shape, then merge the branches.
|
|
230
|
+
|
|
231
|
+
## Compatibility
|
|
232
|
+
|
|
233
|
+
- n8n 1.x and 2.x (Nodes API version 1)
|
|
234
|
+
- Node.js 20.15 or newer for development
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
git clone https://github.com/SilentJMA/n8n-nodes-data-mapper.git
|
|
240
|
+
cd n8n-nodes-data-mapper
|
|
241
|
+
npm install --ignore-scripts
|
|
242
|
+
npm run dev # local n8n at http://localhost:5678 with the node loaded
|
|
243
|
+
npm run lint
|
|
244
|
+
npm test
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the project layout and release process, and [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class DataMapper implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|