skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

    + 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
      ]
    }
    

  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search