knittl
knittl

Reputation: 265221

Transfer or merge only some properties from one JSON file to another with jq

I have two JSON files:

$ jq . a.json b.json 
{
  "id": "ZGVhZGJlZWY=",
  "name": "first file",
  "version": 1,
  "description": "just a simple json file"
}
{
  "version": 2,
  "name": "fake name",
  "dependencies": [
    4,
    2
  ],
  "comment": "I'm just sitting here, ignore me"
}

and want to merge them into a single file (think of file 1 as "template" and file 2 as "actual values"). I don't want to merge all properties, I only want to transfer some properties of the second file (specifically only version and dependencies). version should overwrite the value in the original file and dependencies should be added to the new file. name must not be overwritten and the original name must be kept.

This is the expected result:

{
  "id": "ZGVhZGJlZWY=",
  "name": "first file",
  "version": 2,
  "description": "just a simple json file",
  "dependencies": [
    4,
    2
  ]
}

I know that jq supports the + and * operators to merge or merge recursively, but how can I apply those to only some properties and not all? How can I access both files in my jq program; do I have to preprocess the file and then use --arg in a second jq call?

Obviously, jq '. + {version, dependencies}' a.json b.json does not work. What is the correct program here?

What would the solution look like if description should also be dropped from the output?

Upvotes: 1

Views: 557

Answers (2)

peak
peak

Reputation: 116730

If you want simplicity, brevity, and efficiency, consider:

jq '. + (input|{version, dependencies})' a.json b.json

If the first file might have .dependencies, and if in that case you want to add in the second file's:

jq '. as $a | input as $b | $a + ($b|{version}) | .dependencies += $b.dependencies'

To drop .description, you could append | del(.description) to either of these filters.

Upvotes: 4

knittl
knittl

Reputation: 265221

+ or * can be used here, correct. Let's first see how + works:

$ jq -n '{a:1,b:2} + {b:3,c:4}'
{
  "a": 1,
  "b": 3,
  "c": 4
}
  • Properties only present in the left object are kept
  • Properties of the right object overwrite properties of the left object
  • Properties only present in the right object are added

Perfect, now how to get the objects from two unrelated files? --slurpfile can be used, which reads all JSON entities in the file into an array and puts it into a variable.

$ jq --slurpfile b b.json '. + $b[0]' a.json
{
  "id": "ZGVhZGJlZWY=",
  "name": "fake name",
  "version": 2,
  "description": "just a simple json file",
  "dependencies": [
    4,
    2
  ],
  "comment": "I'm just sitting here, ignore me"
}

We are getting closer, but are not quite there yet. name is overwritten and comment is added; both of which we do not want. To solve this, we can transform the slurped object into a new object which only contains the properties we care about

$ jq --slurpfile b b.json '. + ($b[0] | {version,dependencies})' a.json
{
  "id": "ZGVhZGJlZWY=",
  "name": "first file",
  "version": 2,
  "description": "just a simple json file",
  "dependencies": [
    4,
    2
  ]
}

Now let's address part two of the question: "can some properties of the first file be dropped?"

There are basically two options:

  1. Creating a new object containing only the required properties and then adding the second object (any property part of the second file can be ignored, since it will be added anyway): {id,name} + ($b[0] | {version,dependencies})
  2. Deleting the unneeded properties: del(.description) + ($b[0] | {version,dependencies}) or . + ($b[0] | {version,dependencies}) | del(.description)

Depending on the number of properties you want to keep/drop, one or the other solution might be simpler to use. Creating a new object has the advantage of being able to rename properties in one go.

Executing solution 2:

$ jq --slurpfile b b.json 'del(.description) + ($b[0] | {version,dependencies})' a.json
{
  "id": "ZGVhZGJlZWY=",
  "name": "first file",
  "version": 2,
  "dependencies": [
    4,
    2
  ]
}

Upvotes: 1

Related Questions