json-merger
Version:
Merge JSON (or YAML) files and objects with indicators like $import $remove $replace $merge etc
1,952 lines (1,504 loc) • 30.5 kB
Markdown
# json-merger
Merge JSON (or YAML) files and objects with operations like $import $remove $replace $merge and more.
## Table of Contents:
- [API](#api)
- [`.mergeFile(file: string, config?: Config)`](#mergefilefile-string-config-config)
- [`.mergeFiles(files: string[], config?: Config)`](#mergefilesfiles-string-config-config)
- [`.mergeObject(object: object, config?: Config)`](#mergeobjectobject-object-config-config)
- [`.mergeObjects(objects: object[], config?: Config)`](#mergeobjectsobjects-object-config-config)
- [`Merger(config?: Config)`](#mergerconfig-config)
- [Config](#config)
- [`cwd: string`](#cwd-string)
- [`enableExpressionOperation: boolean`](#enableexpressionoperation-boolean)
- [`errorOnFileNotFound: boolean`](#erroronfilenotfound-boolean)
- [`errorOnRefNotFound: boolean`](#erroronrefnotfound-boolean)
- [`operationPrefix: string`](#operationprefix-string)
- [`params: object`](#params-object)
- [`stringify: boolean`](#stringify-boolean--pretty)
- [`defaultArrayMergeOperation: "combine" | "replace" | "concat"`](#defaultarraymergeoperation-combine--replace--concat)
- [`spaces?: number`](#spaces-number)
- [Operations](#operations)
- [`$import`](#import)
- [`$merge`](#merge)
- [`$remove`](#remove)
- [`$replace`](#replace)
- [`$concat`](#concat)
- [`$combine`](#combine)
- [`$append`](#append)
- [`$prepend`](#prepend)
- [`$insert`](#insert)
- [`$match`](#match)
- [`$move`](#move)
- [`$select`](#select)
- [`$repeat`](#repeat)
- [`$include`](#include)
- [`$expression`](#expression)
- [Scopes](#scopes)
- [Command line interface `json-merger`](#command-line-interface-json-merger)
- [Changelog](#changelog)
- [Roadmap](#roadmap)
## API
### `.mergeFile(file: string, config?: Config)`
**javascript**
```javascript
var jsonMerger = require("json-merger");
var result = jsonMerger.mergeFile("a.json");
```
**a.json:**
```json
{
"$merge": {
"source": {
"$import": "b.json"
},
"with": {
"prop1": {
"$replace": {
"prop1a": "this will replace b.json's property prop1"
}
},
"prop2": {
"prop2a": "this will merge with b.json's property prop2"
}
}
}
}
```
**b.json:**
```json
{
"prop1": {
"prop1b": "will be replaced"
},
"prop2": {
"prop2b": "will be merged"
}
}
```
**result**
```json
{
"prop1": {
"prop1a": "this will replace b.json's property prop1"
},
"prop2": {
"prop2a": "this will merge with b.json's property prop2",
"prop2b": "will be merged"
}
}
```
### `.mergeFiles(files: string[], config?: Config)`
**javascript**
```javascript
var jsonMerger = require("json-merger");
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json:**
```json
{
"a": "some value"
}
```
**b.json:**
```json
{
"b": "some other value"
}
```
**result**
```json
{
"a": "some value",
"b": "some other value"
}
```
### `.mergeObject(object: object, config?: Config)`
**javascript**
```javascript
var jsonMerger = require("json-merger");
var object = {
a: {
aa: "some value",
},
b: {
$import: "b.json",
},
};
var result = jsonMerger.mergeObject(object);
```
**b.json:**
```json
{
"bb": "some other value"
}
```
**result**
```json
{
"a": {
"aa": "some value"
},
"b": {
"bb": "some other value"
}
}
```
### `.mergeObjects(objects: object[], config?: Config)`
**javascript**
```javascript
var jsonMerger = require("json-merger");
var object1 = {
a: [1, 1, 1, 1],
};
var object2 = {
a: [2, 2],
};
var result = jsonMerger.mergeObjects([object1, object2]);
```
**result**
```json
{
"a": [2, 2, 1, 1]
}
```
### `Merger(config?: Config)`
The actual `Merger` class is also exported. The other exports are just shortcut methods.
Using one `Merger` instance has some performance advantages because it will cache previously loaded and processed files.
**javascript**
```javascript
var Merger = require("json-merger").Merger;
var merger = new Merger();
// the first call will load and process "a.json"
var result1 = merger.mergeFile("a.json");
// the second call will return the cached result
var result2 = merger.mergeFile("a.json");
// clear the caches
merger.clearCaches();
```
## Config
```typescript
interface Config {
cwd?: string;
enableExpressionOperation?: boolean;
errorOnFileNotFound?: boolean;
errorOnRefNotFound?: boolean;
operationPrefix?: string;
params?: object;
stringify?: boolean | "pretty";
defaultArrayMergeOperation: "combine" | "replace" | "concat";
}
```
### `cwd: string`
The current working directory when importing files. Defaults to process.cwd().
### `enableExpressionOperation: boolean`
Set this property to `true` to enable the $expression operation.
IMPORTANT: Do not use it to run untrusted code because it uses the node:vm module.
### `errorOnFileNotFound: boolean`
Set this property to `false` to disable throwing errors when an imported file does not exist.
### `errorOnRefNotFound: boolean`
Set this property to `false` to disable throwing errors when an JSON pointer or JSON path does not exist.
### `operationPrefix: string`
Use this property to override the prefix to indicate a property is an operation like $import.
The default prefix is `$`but it is possible to change this to for example`@`to use keywords like`@import`.
### `params: object`
Object that will be available in [`$expression`](#expression) operations as `$params` variable.
### `stringify: boolean | "pretty"`
Set this property to `true` to stringify the JSON result. Set the property to `"pretty"` if the output should be pretty printed.
### `defaultArrayMergeOperation: "combine" | "replace" | "concat"`
Set this property to override default merge operation.
Default value is set to [`"combine"`](#combine). Possible values are:
- [`"replace"`](#replace)
- [`"concat"`](#concat)
- [`"combine"`](#combine)
### `spaces?: number`
Set this property to indent with spaces instead of tabs when prettifying json.
Invalid numbers disables the configuration and resets to tab character. Supports numbers between 0 and 10.
Lower than 0 assumes 0 and higher than 10 assumes 10.
## Operations
### `$import`
Use `$import` to import other JSON or YAML files.
Files imported with `$import` are processed before the result is returned.
```json
{
"$import": "a.json"
}
```
[JSON reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03) syntax is supported. The following example will import the first array item from the `someArray` property in `a.json`.
```json
{
"$import": "a.json#/someArray/0"
}
```
When defined as an array, `$import` will process and merge the files in order before returning the result.
```json
{
"$import": ["a.json", "b.yaml", "c.json"]
}
```
When importing a file it is also possible to provide a different `$params` object.
Setting this property will override the `Config.params` property.
```json
{
"$import": {
"path": "a.json",
"params": {
"prop": "some value that will be available in a.json as $params.prop"
}
}
}
```
The object syntax is also supported in an array.
```json
{
"$import": [
{
"path": "a.json",
"params": {
"prop": "value1"
}
},
{
"path": "a.json",
"params": {
"prop": "value2"
}
}
]
}
```
Use [`$include`](#include) to process a file in the current scope.
### `$merge`
Use the `$merge` operation to merge objects and arrays.
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"$merge": {
"source": {
"a": {
"aa": "some value"
}
},
"with": {
"a": {
"bb": "some other value"
}
}
}
}
```
**result**
```json
{
"a": {
"aa": "some value",
"bb": "some other value"
}
}
```
#### Merging with other files
The `$merge` operation is often used with the `$import` operation to merge other files from within the JSON itself.
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"$merge": {
"source": {
"$import": "b.json"
},
"with": {
"a": {
"bb": "some other value"
}
}
}
}
```
**b.json**
```json
{
"a": {
"aa": "some value"
}
}
```
**result**
```json
{
"a": {
"aa": "some value",
"bb": "some other value"
}
}
```
### `$remove`
Use the `$remove` operation to remove properties and array items.
#### Remove object properties
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"prop1": {
"prop1a": "some value"
},
"prop2": {
"prop2a": "some other value"
}
}
```
**b.json**
```json
{
"prop2": {
"$remove": true
}
}
```
**result**
```json
{
"prop1": {
"prop1a": "some value"
}
}
```
#### Remove array items
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$remove": true
},
{
"$remove": true
}
]
}
```
**result**
```json
{
"someArray": [3]
}
```
### `$replace`
Use the `$replace` operation to replace properties and array items.
#### Replace object properties
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"prop1": {
"prop1a": "some value"
},
"prop2": {
"prop2a": "some other value"
}
}
```
**b.json**
```json
{
"prop2": {
"$replace": {
"prop2b": "replaced value"
}
}
}
```
**result**
```json
{
"prop1": {
"prop1a": "some value"
},
"prop2": {
"prop2b": "replaced value"
}
}
```
#### Replace array items
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [
{
"a": 1
},
{
"b": 2
}
]
}
```
**b.json**
```json
{
"someArray": [
{
"$replace": {
"c": 3
}
}
]
}
```
**result**
```json
{
"someArray": [
{
"c": 3
},
{
"b": 2
}
]
}
```
### `$concat`
Use the `$concat` operation to concatenate two arrays.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1]
}
```
**b.json**
```json
{
"someArray": {
"$concat": [2]
}
}
```
**result**
```json
{
"someArray": [1, 2]
}
```
### `$combine`
Use the `$combine` operation to combine two arrays.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": {
"$combine": [3, 3]
}
}
```
**result**
```json
{
"someArray": [3, 3, 3]
}
```
### `$append`
Use the `$append` operation to append an item to an array.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$append": 4
}
]
}
```
**result**
```json
{
"someArray": [1, 2, 3, 4]
}
```
### `$prepend`
Use the `$prepend` operation to prepend an item to an array.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$prepend": 4
}
]
}
```
**result**
```json
{
"someArray": [4, 1, 2, 3]
}
```
### `$insert`
Use the `$insert` operation to insert an item to an array.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$insert": {
"index": 1,
"value": 4
}
}
]
}
```
**result**
```json
{
"someArray": [1, 4, 2, 3]
}
```
#### Insert as last item
Set `$insert.index` to `-` to insert an item at the end of the array.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$insert": {
"index": "-",
"value": 4
}
}
]
}
```
**result**
```json
{
"someArray": [1, 2, 3, 4]
}
```
#### Insert before the last item
A negative `$insert.index` can be used, indicating an offset from the end of the array.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$insert": {
"index": -1,
"value": 4
}
}
]
}
```
**result**
```json
{
"someArray": [1, 2, 4, 3]
}
```
### `$match`
Use the `$match` operation to search for a specific array item and merge with that item.
#### Match by index
Use `$match.index` to match an array item by index.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$match": {
"index": 1,
"value": 4
}
}
]
}
```
**result**
```json
{
"someArray": [1, 4, 3]
}
```
#### Match by JSON pointer
Use `$match.path` to match an array item with a JSON pointer.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$match": {
"path": "/1",
"value": 4
}
}
]
}
```
**result**
```json
{
"someArray": [1, 4, 3]
}
```
#### Match by JSON path query
Use `$match.query` to match an array item with a [JSON path](https://www.npmjs.com/package/jsonpath) query.
The following example will search for an array item containing the value `2` and merge it with the value `4`.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$match": {
"query": "$[?(@ == 2)]",
"value": 4
}
}
]
}
```
**result**
```json
{
"someArray": [1, 4, 3]
}
```
### `$move`
Use the `$move` operation to move an array item.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$move": 1
}
]
}
```
**result**
```json
{
"someArray": [2, 1, 3]
}
```
#### Move a matched array item
Use the `$match` operation in conjunction with the `$move` operation to move a specific array item.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$match": {
"index": 0,
"value": {
"$move": 1
}
}
}
]
}
```
**result**
```json
{
"someArray": [2, 1, 3]
}
```
#### Move a matched array item to the end
Use `-` as `$move.index` value to move an array item to the end.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$match": {
"index": 0,
"value": {
"$move": "-"
}
}
}
]
}
```
**result**
```json
{
"someArray": [2, 3, 1]
}
```
#### Move and merge a matched array item
Use `$move.value` to not only move the item but also merge it with a value.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [
{
"a": 1
},
{
"a": 2
},
{
"a": 3
}
]
}
```
**b.json**
```json
{
"someArray": [
{
"$match": {
"query": "$[?(@.a == 3)]",
"value": {
"$move": {
"index": 0,
"value": {
"b": 3
}
}
}
}
}
]
}
```
**result**
```json
{
"someArray": [
{
"a": 3,
"b": 3
},
{
"a": 1
},
{
"a": 2
}
]
}
```
### `$select`
Use the `$select` operation to select one or multiple values.
Be careful not to create an endless loop by selecting a parent property.
#### Select by JSON pointer
More information about JSON pointers can be found in the [JSON pointer specification](https://tools.ietf.org/html/rfc6901).
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"prop": {
"$select": "/otherProp"
},
"otherProp": "Should be the value of prop"
}
```
**result**
```json
{
"prop": "Should be the value of prop",
"otherProp": "Should be the value of prop"
}
```
#### Use `$select.query` to select by JSON path query
More information about JSON path queries can be found in the [JSON path documentation](https://www.npmjs.com/package/jsonpath).
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"prop": {
"$select": {
"query": "$.someArray[*]"
}
},
"someArray": [1, 2, 3]
}
```
**result**
```json
{
"prop": 1,
"someArray": [1, 2, 3]
}
```
#### Use `$select.multiple` to select multiple values
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"prop": {
"$select": {
"query": "$.someArray[?(@ < 3)]",
"multiple": true
}
},
"someArray": [1, 2, 3]
}
```
**result**
```json
{
"prop": [1, 2]
}
```
#### Use `$select.from` to select from an object
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"prop": {
"$select": {
"from": {
"$import": "b.json"
},
"path": "/someArray/2"
}
}
}
```
**b.json**
```json
{
"someArray": [1, 2, 3]
}
```
**result**
```json
{
"prop": 3
}
```
### `$repeat`
Use the `$repeat` operation to repeat a value.
#### Repeat with `$repeat.to`
**operation**
```json
{
"$repeat": {
"from": 1,
"to": 4,
"value": "repeat"
}
}
```
**result**
```json
["repeat", "repeat", "repeat"]
```
#### Repeat with `$repeat.through`
The current value is available on the scope as `$repeat.value` variable.
**operation**
```json
{
"$repeat": {
"from": 1,
"through": 4,
"value": {
"$expression": "$repeat.value"
}
}
}
```
**result**
```json
[1, 2, 3, 4]
```
#### Repeat with `$repeat.step`
**operation**
```json
{
"$repeat": {
"from": 0,
"through": 10,
"step": 5,
"value": {
"$expression": "$repeat.value"
}
}
}
```
**result**
```json
[0, 5, 10]
```
#### Repeat with `$repeat.range`
**operation**
```json
{
"$repeat": {
"range": "0:-2, 10, 20:30:5",
"value": {
"$expression": "$repeat.value"
}
}
}
```
**result**
```json
[0, -1, -2, 10, 20, 25, 30]
```
#### Repeat with `$repeat.in` as array
**operation**
```json
{
"$repeat": {
"in": ["a", "b"],
"value": {
"$expression": "$repeat.value"
}
}
}
```
**result**
```json
["a", "b"]
```
#### Repeat with `$repeat.in` as object
The current key is available on the scope as `$repeat.key` variable.
**operation**
```json
{
"$repeat": {
"in": {
"keyA": "valueA",
"keyB": "valueB"
},
"value": {
"$expression": "{key: $repeat.key, value: $repeat.value}"
}
}
}
```
**result**
```json
[
{ "key": "keyA", "value": "valueA" },
{ "key": "keyB", "value": "valueB" }
]
```
#### Getting the current index
The current index is available on the scope as `$repeat.index` variable.
**operation**
```json
{
"$repeat": {
"range": "1:2",
"value": {
"$expression": "$repeat.index"
}
}
}
```
**result**
```json
[0, 1]
```
#### Nested repeat
Use `$parent` to get to the parent scope containing the parent `$repeat`.
**operation**
```json
{
"$repeat": {
"range": "0:1",
"value": {
"$repeat": {
"range": "0:1",
"value": {
"$expression": "$parent.$repeat.index + '.' + $repeat.index"
}
}
}
}
}
```
**result**
```json
["0.0", "0.1", "1.0", "1.1"]
```
### `$include`
Use `$include` to load other JSON or YAML files and process them in the current scope.
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"someArray": [1, 2, 3]
}
```
**b.json**
```json
{
"someArray": [
{
"$include": "remove.json"
}
]
}
```
**remove.json**
```json
{
"$remove": true
}
```
**result**
```json
{
"someArray": [2, 3]
}
```
### `$expression`
Use the `$expression` operation to calculate a value with the help of a JavaScript expression.
The expression has access to the standard built-in JavaScript objects, the current [scope](#scopes) and optionally an `$input` variable.
By default this operation is disabled because it allows executing untrusted code which could introduce a security risk.
It can be enabled by setting the [`enableExpressionOperation`](#enableexpressionoperation-boolean) option.
#### Calculate a value
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json");
```
**a.json**
```json
{
"prop": {
"$expression": "1 + 2"
}
}
```
**result**
```json
{
"prop": 3
}
```
#### Calculate a value using `$expression.input`
**javascript**
```javascript
var result = jsonMerger.mergeFile("b.json");
```
**a.json**
```json
{
"add": 2
}
```
**b.json**
```json
{
"prop": {
"$expression": {
"expression": "1 + $input",
"input": {
"$import": "a.json#/add"
}
}
}
}
```
**result**
```json
{
"prop": 3
}
```
#### Calculate a value using the scope $targetProperty
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```json
{
"prop": 1
}
```
**b.json**
```json
{
"prop": {
"$expression": "$targetProperty + 2"
}
}
```
**result**
```json
{
"prop": 3
}
```
#### Calculate a value using the scope $params
**javascript**
```javascript
var result = jsonMerger.mergeFile("a.json", {
params: {
add: 2,
},
});
```
**a.json**
```json
{
"prop": {
"$expression": "1 + $params.add"
}
}
```
**result**
```json
{
"prop": 3
}
```
## Scopes
Scopes can be created while processing operation properties.
If for example a `$merge.with` is being processed then the merger will create a new scope for the `$merge.with` property.
Or if a `$repeat.value` property is being processed a new scope is created for the `$repeat.value` property.
A scope always has a `$source` property but not necessarily a `$target` property.
When we are merging object A with object B, then the `$target` property in the scope of object A is `undefined` because object A is not merged with anything.
It does have a `$source` property referring to object A itself.
Object B on the other hand has the processed object A as `$target` because object B is being merged with object A.
The `$source` property in the scope of object B refers to object B.
If object B had defined a `$merge` operation, then the merger would create a new scope for the `$merge.source` property and a new scope for the `$merge.with` property.
The `$target` within the `$merge.source` scope would be `undefined` because `$merge.source` is not merged with anything.
The `$target` within the `$merge.with` scope is the processed `$merge.source` because `$merge.with` is being merged with `$merge.source`.
The result of the `$merge` operation will eventually be merged with object A.
When in the `$merge.source` scope it is possible to get to the root (object B) scope using the `$root` property or to a parent scope using the `$parent` property.
```typescript
interface Scope {
$params?: any; // $params properties in current scope
$parent?: Scope; // reference to parent scope
$repeat?: ScopeRepeat; // $repeat properties in current scope
$root: Scope; // reference to root scope
$source: any; // reference to the source object
$target?: any; // reference to the target object
}
```
#### Example
**javascript**
```javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
```
**a.json**
```js
// this is the root scope
{
"prop1": {
"$expression": "$target" // refers to undefined because a.json has no target
},
"prop2": {
"$expression": "$targetProperty" // refers to undefined because a.json has no target
},
"prop3": {
"$expression": "$source" // refers to the unprocessed a.json
},
"prop4": {
"$expression": "$sourceProperty" // refers to the unprocessed a.json#/prop4
},
"prop5": {
"$expression": "$root.$target" // refers to undefined because a.json has no target
},
"prop6": {
"$expression": "$root.$source" // refers to the unprocessed a.json
},
"prop7": {
"$expression": "$parent" // refers to undefined because the current scope has no parent scope
},
}
```
**b.json**
```js
// this is the root scope
{
"prop1": {
"$expression": "$target" // refers to the processed a.json
},
"prop2": {
"$expression": "$targetProperty" // refers to the processed a.json#/prop2
},
"prop3": {
"$expression": "$source" // refers to the unprocessed b.json
},
"prop4": {
"$expression": "$sourceProperty" // refers to the unprocessed b.json#/prop4
},
"prop5": {
"$merge": {
"source": { // $merge.source creates a new scope
"prop1": {
"$expression": "$target" // refers to undefined because b.json#/prop5/$merge/source has no target
},
"prop2": {
"$expression": "$targetProperty" // refers to undefined because b.json#/prop5/$merge/source has no target
},
"prop3": {
"$expression": "$source" // refers to the unprocessed b.json#/prop5/$merge/source
},
"prop4": {
"$expression": "$sourceProperty" // refers to the unprocessed b.json#/prop5/$merge/source/prop4
}
"prop5": {
"$expression": "$root.$target" // refers to the processed a.json
}
"prop6": {
"$expression": "$root.$source" // refers to the unprocessed b.json
},
"prop7": {
"$expression": "$parent.$target" // refers to the processed a.json
}
"prop8": {
"$expression": "$parent.$source" // refers to the unprocessed b.json
}
},
"with": { // $merge.with creates a new scope
"prop1": {
"$expression": "$target" // refers to the processed b.json#/prop5/$merge/source
},
"prop2": {
"$expression": "$targetProperty" // refers to the processed b.json#/prop5/$merge/source/prop2
},
"prop3": {
"$expression": "$source" // refers to the unprocessed b.json#/prop5/$merge/with
},
"prop4": {
"$expression": "$sourceProperty" // refers to the unprocessed b.json#/prop5/$merge/with/prop4
},
"prop5": {
"$expression": "$root.$target" // refers to the processed a.json
},
"prop6": {
"$expression": "$root.source" // refers to the unprocessed b.json
},
"prop7": {
"$expression": "$parent.$target" // refers to the processed a.json
},
"prop8": {
"$expression": "$parent.source" // refers to the unprocessed b.json
}
}
}
},
"prop6": {
"$repeat": {
"range": "0:1",
"value": { // $repeat.value creates a new scope with a $repeat property on it
"$repeat": {
"range": "0:1",
"value": { // $repeat.value creates a new scope with a $repeat property on it
"$expression": "'This is item ' + $parent.$repeat.index + '.' + $repeat.index"
}
}
}
}
}
}
```
---
## Command line interface `json-merger`
You can use `json-merger` as a command line tool:
```
Usage: json-merger [options] <file ...>
Options:
-V, --version output the version number
-p, --pretty pretty-print the output json
-o, --output [file] the output file. Defaults to stdout
--op, --operation-prefix [prefix] the operation prefix. Defaults to $
--am, --default-array-merge-operation [operation] the default array merge operation. Defaults to combine
-s, --spaces <value> Use number of spaces instead of tab when pretty-printing json.
--enable-expression-operation [value] enables expressions. Do not use it to run untrusted code because it uses the node:vm module. Defaults to false
--error-on-file-not-found [value] throw an error if a file is not found. Defaults to true
--error-on-ref-not-found [value] throw an error if a JSON pointer or JSON path is not found. Defaults to true
-h, --help output usage information
```
Usage:
```sh
json-merger a.json > result.json
json-merger --output result.json a.json
json-merger --output result.json --pretty a.json
```
Install `json-merger` globally to be able to use the command line interface.
```sh
npm install -g json-merger
```
## Changelog
### 2.0.0
- Changed emitted JavaScript target to ES6.
- The `$expression` operation is using the `node:vm` package instead of the deprecated `vm2` package.
- The `$expression` operation is disabled by default because it allows executing untrusted code.
- Added the `enableExpressionOperation` configuration option to enable the `$expression` operation.
- Added the `--enable-expression-operation` CLI option to enable the `$expression` operation.
### 2.1.0
- Added the `--spaces` CLI option to allow pretty formatting with spaces instead of tabs.
### 3.0.0
- The `-op` and `-am` CLI options have been renamed to `--op` and `--am`.
## Roadmap
- Add configurable file resolvers to import files from different sources.
- Add configurable (de)serializers to import and export different file formats.