
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
json-merger
Advanced tools
Merge JSON (or YAML) files and objects with indicators like $import $remove $replace $merge etc
Merge JSON (or YAML) files and objects with operations like $import $remove $replace $merge and more.
.mergeFile(file: string, config?: Config)javascript
var jsonMerger = require("json-merger");
var result = jsonMerger.mergeFile("a.json");
a.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:
{
"prop1": {
"prop1b": "will be replaced"
},
"prop2": {
"prop2b": "will be merged"
}
}
result
{
"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
var jsonMerger = require("json-merger");
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json:
{
"a": "some value"
}
b.json:
{
"b": "some other value"
}
result
{
"a": "some value",
"b": "some other value"
}
.mergeObject(object: object, config?: Config)javascript
var jsonMerger = require("json-merger");
var object = {
a: {
aa: "some value",
},
b: {
$import: "b.json",
},
};
var result = jsonMerger.mergeObject(object);
b.json:
{
"bb": "some other value"
}
result
{
"a": {
"aa": "some value"
},
"b": {
"bb": "some other value"
}
}
.mergeObjects(objects: object[], config?: Config)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
{
"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
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();
interface Config {
cwd?: string;
enableExpressionOperation?: boolean;
errorOnFileNotFound?: boolean;
errorOnRefNotFound?: boolean;
operationPrefix?: string;
params?: object;
stringify?: boolean | "pretty";
defaultArrayMergeOperation: "combine" | "replace" | "concat";
}
cwd: stringThe current working directory when importing files. Defaults to process.cwd().
enableExpressionOperation: booleanSet 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: booleanSet this property to false to disable throwing errors when an imported file does not exist.
errorOnRefNotFound: booleanSet this property to false to disable throwing errors when an JSON pointer or JSON path does not exist.
operationPrefix: stringUse 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: objectObject that will be available in $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". Possible values are:
spaces?: numberSet 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.
$importUse $import to import other JSON or YAML files.
Files imported with $import are processed before the result is returned.
{
"$import": "a.json"
}
JSON reference syntax is supported. The following example will import the first array item from the someArray property in a.json.
{
"$import": "a.json#/someArray/0"
}
When defined as an array, $import will process and merge the files in order before returning the result.
{
"$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.
{
"$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.
{
"$import": [
{
"path": "a.json",
"params": {
"prop": "value1"
}
},
{
"path": "a.json",
"params": {
"prop": "value2"
}
}
]
}
Use $include to process a file in the current scope.
$mergeUse the $merge operation to merge objects and arrays.
javascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"$merge": {
"source": {
"a": {
"aa": "some value"
}
},
"with": {
"a": {
"bb": "some other value"
}
}
}
}
result
{
"a": {
"aa": "some value",
"bb": "some other value"
}
}
The $merge operation is often used with the $import operation to merge other files from within the JSON itself.
javascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"$merge": {
"source": {
"$import": "b.json"
},
"with": {
"a": {
"bb": "some other value"
}
}
}
}
b.json
{
"a": {
"aa": "some value"
}
}
result
{
"a": {
"aa": "some value",
"bb": "some other value"
}
}
$removeUse the $remove operation to remove properties and array items.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"prop1": {
"prop1a": "some value"
},
"prop2": {
"prop2a": "some other value"
}
}
b.json
{
"prop2": {
"$remove": true
}
}
result
{
"prop1": {
"prop1a": "some value"
}
}
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$remove": true
},
{
"$remove": true
}
]
}
result
{
"someArray": [3]
}
$replaceUse the $replace operation to replace properties and array items.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"prop1": {
"prop1a": "some value"
},
"prop2": {
"prop2a": "some other value"
}
}
b.json
{
"prop2": {
"$replace": {
"prop2b": "replaced value"
}
}
}
result
{
"prop1": {
"prop1a": "some value"
},
"prop2": {
"prop2b": "replaced value"
}
}
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [
{
"a": 1
},
{
"b": 2
}
]
}
b.json
{
"someArray": [
{
"$replace": {
"c": 3
}
}
]
}
result
{
"someArray": [
{
"c": 3
},
{
"b": 2
}
]
}
$concatUse the $concat operation to concatenate two arrays.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1]
}
b.json
{
"someArray": {
"$concat": [2]
}
}
result
{
"someArray": [1, 2]
}
$combineUse the $combine operation to combine two arrays.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": {
"$combine": [3, 3]
}
}
result
{
"someArray": [3, 3, 3]
}
$appendUse the $append operation to append an item to an array.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$append": 4
}
]
}
result
{
"someArray": [1, 2, 3, 4]
}
$prependUse the $prepend operation to prepend an item to an array.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$prepend": 4
}
]
}
result
{
"someArray": [4, 1, 2, 3]
}
$insertUse the $insert operation to insert an item to an array.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$insert": {
"index": 1,
"value": 4
}
}
]
}
result
{
"someArray": [1, 4, 2, 3]
}
Set $insert.index to - to insert an item at the end of the array.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$insert": {
"index": "-",
"value": 4
}
}
]
}
result
{
"someArray": [1, 2, 3, 4]
}
A negative $insert.index can be used, indicating an offset from the end of the array.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$insert": {
"index": -1,
"value": 4
}
}
]
}
result
{
"someArray": [1, 2, 4, 3]
}
$matchUse the $match operation to search for a specific array item and merge with that item.
Use $match.index to match an array item by index.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$match": {
"index": 1,
"value": 4
}
}
]
}
result
{
"someArray": [1, 4, 3]
}
Use $match.path to match an array item with a JSON pointer.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$match": {
"path": "/1",
"value": 4
}
}
]
}
result
{
"someArray": [1, 4, 3]
}
Use $match.query to match an array item with a JSON path query.
The following example will search for an array item containing the value 2 and merge it with the value 4.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$match": {
"query": "$[?(@ == 2)]",
"value": 4
}
}
]
}
result
{
"someArray": [1, 4, 3]
}
$moveUse the $move operation to move an array item.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$move": 1
}
]
}
result
{
"someArray": [2, 1, 3]
}
Use the $match operation in conjunction with the $move operation to move a specific array item.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$match": {
"index": 0,
"value": {
"$move": 1
}
}
}
]
}
result
{
"someArray": [2, 1, 3]
}
Use - as $move.index value to move an array item to the end.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$match": {
"index": 0,
"value": {
"$move": "-"
}
}
}
]
}
result
{
"someArray": [2, 3, 1]
}
Use $move.value to not only move the item but also merge it with a value.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [
{
"a": 1
},
{
"a": 2
},
{
"a": 3
}
]
}
b.json
{
"someArray": [
{
"$match": {
"query": "$[?(@.a == 3)]",
"value": {
"$move": {
"index": 0,
"value": {
"b": 3
}
}
}
}
}
]
}
result
{
"someArray": [
{
"a": 3,
"b": 3
},
{
"a": 1
},
{
"a": 2
}
]
}
$selectUse the $select operation to select one or multiple values.
Be careful not to create an endless loop by selecting a parent property.
More information about JSON pointers can be found in the JSON pointer specification.
javascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"prop": {
"$select": "/otherProp"
},
"otherProp": "Should be the value of prop"
}
result
{
"prop": "Should be the value of prop",
"otherProp": "Should be the value of prop"
}
$select.query to select by JSON path queryMore information about JSON path queries can be found in the JSON path documentation.
javascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"prop": {
"$select": {
"query": "$.someArray[*]"
}
},
"someArray": [1, 2, 3]
}
result
{
"prop": 1,
"someArray": [1, 2, 3]
}
$select.multiple to select multiple valuesjavascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"prop": {
"$select": {
"query": "$.someArray[?(@ < 3)]",
"multiple": true
}
},
"someArray": [1, 2, 3]
}
result
{
"prop": [1, 2]
}
$select.from to select from an objectjavascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"prop": {
"$select": {
"from": {
"$import": "b.json"
},
"path": "/someArray/2"
}
}
}
b.json
{
"someArray": [1, 2, 3]
}
result
{
"prop": 3
}
$repeatUse the $repeat operation to repeat a value.
$repeat.tooperation
{
"$repeat": {
"from": 1,
"to": 4,
"value": "repeat"
}
}
result
["repeat", "repeat", "repeat"]
$repeat.throughThe current value is available on the scope as $repeat.value variable.
operation
{
"$repeat": {
"from": 1,
"through": 4,
"value": {
"$expression": "$repeat.value"
}
}
}
result
[1, 2, 3, 4]
$repeat.stepoperation
{
"$repeat": {
"from": 0,
"through": 10,
"step": 5,
"value": {
"$expression": "$repeat.value"
}
}
}
result
[0, 5, 10]
$repeat.rangeoperation
{
"$repeat": {
"range": "0:-2, 10, 20:30:5",
"value": {
"$expression": "$repeat.value"
}
}
}
result
[0, -1, -2, 10, 20, 25, 30]
$repeat.in as arrayoperation
{
"$repeat": {
"in": ["a", "b"],
"value": {
"$expression": "$repeat.value"
}
}
}
result
["a", "b"]
$repeat.in as objectThe current key is available on the scope as $repeat.key variable.
operation
{
"$repeat": {
"in": {
"keyA": "valueA",
"keyB": "valueB"
},
"value": {
"$expression": "{key: $repeat.key, value: $repeat.value}"
}
}
}
result
[
{ "key": "keyA", "value": "valueA" },
{ "key": "keyB", "value": "valueB" }
]
The current index is available on the scope as $repeat.index variable.
operation
{
"$repeat": {
"range": "1:2",
"value": {
"$expression": "$repeat.index"
}
}
}
result
[0, 1]
Use $parent to get to the parent scope containing the parent $repeat.
operation
{
"$repeat": {
"range": "0:1",
"value": {
"$repeat": {
"range": "0:1",
"value": {
"$expression": "$parent.$repeat.index + '.' + $repeat.index"
}
}
}
}
}
result
["0.0", "0.1", "1.0", "1.1"]
$includeUse $include to load other JSON or YAML files and process them in the current scope.
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"someArray": [1, 2, 3]
}
b.json
{
"someArray": [
{
"$include": "remove.json"
}
]
}
remove.json
{
"$remove": true
}
result
{
"someArray": [2, 3]
}
$expressionUse 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 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 option.
javascript
var result = jsonMerger.mergeFile("a.json");
a.json
{
"prop": {
"$expression": "1 + 2"
}
}
result
{
"prop": 3
}
$expression.inputjavascript
var result = jsonMerger.mergeFile("b.json");
a.json
{
"add": 2
}
b.json
{
"prop": {
"$expression": {
"expression": "1 + $input",
"input": {
"$import": "a.json#/add"
}
}
}
}
result
{
"prop": 3
}
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
{
"prop": 1
}
b.json
{
"prop": {
"$expression": "$targetProperty + 2"
}
}
result
{
"prop": 3
}
javascript
var result = jsonMerger.mergeFile("a.json", {
params: {
add: 2,
},
});
a.json
{
"prop": {
"$expression": "1 + $params.add"
}
}
result
{
"prop": 3
}
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.
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
}
javascript
var result = jsonMerger.mergeFiles(["a.json", "b.json"]);
a.json
// 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
// 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"
}
}
}
}
}
}
json-mergerYou 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:
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.
npm install -g json-merger
$expression operation is using the node:vm package instead of the deprecated vm2 package.$expression operation is disabled by default because it allows executing untrusted code.enableExpressionOperation configuration option to enable the $expression operation.--enable-expression-operation CLI option to enable the $expression operation.--spaces CLI option to allow pretty formatting with spaces instead of tabs.-op and -am CLI options have been renamed to --op and --am.FAQs
Merge JSON (or YAML) files and objects with indicators like $import $remove $replace $merge etc
The npm package json-merger receives a total of 30,608 weekly downloads. As such, json-merger popularity was classified as popular.
We found that json-merger demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.