PingCrosby
PingCrosby

Reputation: 410

How to deep merge 2 files with jq for arrays

I am trying to merge the following two json files; but I only seem to be able to partially merge; as soon as an element is within an array the reduce fails.

{
    "value1": 200,
    "timestamp": 1382461861,
    "deployment": [
        {
            "component": "whatever",
            "containers": "key value"
        }
    ]
}

and

{
    "status": 200,
    "timestamp": 1382461861,
    "deployment": [
        {
            "autoscaling": {
                "maxReplicas": 1,
                "minReplicas": 1,
                "targetCPUUtilizationPercentage": 40
            }
        }
    ]
}

Using jq -s 'reduce .[] as $item({}; . * $item)' x.json x2.json does not work if the input is an array; instead I get a partial merge but not a deep merge

{
  "value1": 200,
  "timestamp": 1382461861,
  "deployment": [
    {
      "autoscaling": {
        "maxReplicas": 1,
        "minReplicas": 1,
        "targetCPUUtilizationPercentage": 40
      }
    }
  ],
  "status": 200
}

The expected output would be

{
    "value1": 200,
    "timestamp": 1382461861,
    "deployment": [
        {
            "component": "whatever",
            "containers": "key value",

            "autoscaling": {
                "maxReplicas": 1,
                "minReplicas": 1,
                "targetCPUUtilizationPercentage": 40
            }
        }
    ],
    "status": 200,
}

Can i anyone suggest where I may be going wrong

Upvotes: 1

Views: 293

Answers (1)

peak
peak

Reputation: 116870

It looks like you will have to define your own "merge" filter.

Given your sample inputs, the following does produce the result you ask for, but it should probably be taken as a starting point as you refine your specification:

def merge($a; $b): 
   if ($a|type) == "object" and ($b|type) == "object" 
       then reduce (($a + $b)|keys_unsorted[]) as $k ({}; 
         .[$k] = merge($a[$k]; $b[$k]))
   elif ($a|type) =="array" and ($b|type) == "array" then  $a + $b | add
   elif $b == null then $a
   else $b
   end;

For efficiency, you might wish to consider invoking this as merge(input;input), using the -n command-line option instead of -s.

Upvotes: 2

Related Questions