Merge 2 JSON objects with jq

Viewed 442

I have 2 json objects in 2 variables V1 and V2.

V1 is

{
  "Parameters: {
     "a": "value-of-a",
     "b": "value-of-b"
  }
}

V2 is

{
  "Parameters": {
     "a": "new-value-of-a",
     "c": "value-of-c"
  }
}

I want to override the V1's values with values from V2. I want to generate a new JSON that looks like this:

Expected

{
   "Parameters": {
      "a": "new-value-of-a",
      "b": "value-of-b"
   }
}

I tried the following:

(
echo '{ "Parameters": { "a": "value-of-a", "b": "value-of-b" } }'\
'{ "Parameters": { "a": "new-value-of-a", "c": "value-of-c" } }'\
| jq --slurp 'reduce .[] as $item ({}; . * $item)'
)

but this generates

Actual

{
   "Parameters": {
      "a": "new-value-of-a",
      "b": "value-of-b",
      "c": "value-of-c"
   }
}

The problem is that I don't want the nodes in V2 that don't exist in V1 to appear in the result. Can anyone help me?

3 Answers

If $.Parameter object is simple as in your example

jq \
 --argjson a '{ "Parameters": { "a": "value-of-a", "b": "value-of-b" } }' \
 --argjson b '{ "Parameters": { "a": "new-value-of-a", "c": "value-of-c" } }' \
 -n '
   $a | .Parameters | keys_unsorted as $whitelist 
 | $b | .Parameters | with_entries( select(.key | IN($whitelist[])) ) as $update
 | $a | .Parameters += $update '

If you have two files, you could (for example) use input twice instead of --argjson.

Here's a solution that’s both simple and efficient:

jq -n '
   input.Parameters as $dict
   | input
   | .Parameters |= reduce keys_unsorted[] as $k (.;
       if ($dict|has($k)) then .[$k] = $dict[$k] else . end)
' v2.json v1.json 

If the two values are shell variables, you can use --argjson or simply echo them:

echo "$v2" "$v1" | jq -n '...'

There are other ways to provide the two values, but in general it’s best to avoid the -s command-line option.

Here's a reduce-free solution that's conceptually simple and scales well:

jq -n '
   # Emit the object with key-value pairs of $o2 
   # for keys that appear in both . and $o2
   def commonKeys($o2):
     keys_unsorted as $k1
     | ($o2|keys_unsorted) as $k2
     | [($k1 - ($k1-$k2))[] | {(.): $o2[.]}] | add
   ;
   input.Parameters as $dict
   | input
   | .Parameters |= (. + commonKeys($dict))

' v2.json v1.json 
Related