Friday, October 28, 2022

[SOLVED] JQ Library: Overriding existing keys of an JSON object with another object keys

Issue

I am new to the JQ Library and I cannot figure out how to replace values in config_new.json with values from keys that exist in both config.json and config_new.json, recursively, without copying any other attributes from config.json.

Basically having:

// config_new.json
{
  "name": "testName",
  "age": "tooOld",
  "properties": {
      "title": "Mr",
      "fruits": ["apple", "banana"]
  },
}
// config.json
{
  "newName": "changedName",
  "age": "tooYoung",
  "properties": {
      "title": "Mr",
      "height": "tooTall",
      "fruits": ["banana"]
  },
  "certificate": "present"
}
// expected result
{
  "name": "testName",
  "age": "tooYoung",
  "properties": {
      "title": "Mr",
      "height": "tooTall",
      "fruits": ["banana"]
  },
}

So I am trying to override the values in config_new.json only with known values in config.json.

I have tried using

jq -s '.[0] * .[1]' config_new.json config.json

but this works only partially, because it also copies the key-value pairs that do not exist in config_new.json:

{
  "name": "testName",
  "newName": "changedName", // This should not be here
  "age": "tooYoung", // This was replaced from config.json
  "properties": {
      "title": "Mr",
      "height": "tooTall", // This should not be here
      "fruits": ["banana"]
  },
}

Could someone help me?


Solution

Here's a jq answer that recursively merges objects according to your criteria:

jq -s '
    def merge($source):
        . as $target
        | reduce ($source | keys | map(select(in($target))))[] as $key ($target;
            .[$key] = if (.[$key] | type) == "object"
                          then .[$key] | merge($source[$key])
                          else $source[$key]
                      end
        )
    ;

    . as [$new, $old]
    | $new | merge($old)
' config_new.json config.json

outputs

{
  "name": "testName",
  "age": "tooYoung",
  "properties": {
    "title": "Mr",
    "fruits": [
      "banana"
    ]
  }
}

This takes config_new.json as the "target" and config.json as the "source". To merge $source into $target, iterate over the $source keys that are in $target, then look at the datatype of the key's value: if it's an object, then recursively merge those objects, otherwise put the $source's value into $target for that key.



Answered By - glenn jackman
Answer Checked By - Candace Johnson (WPSolving Volunteer)