ishu
ishu

Reputation: 3

Add JSON Object at specific location

Here are my two json files content:

file1.json

{
  "nodes": [
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-A"
    },
    {
      "pm_addr": "192.100.0.6",
      "name": "TB1-OSC-B"
    },
    {
      "pm_addr": "192.100.0.7",
      "name": "TB1-OSC-C"
    }
  ]
}

file2.json

{
  "nodes": [
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-D"
    }
  ]
}

I want to add the contents of file2.json to file1.json but after the json object : TB1-OSC-B i.e., second json entry. In this case the entry is present at second location but it may be present any where else aswell. My final json file should look like this:

file3.json

{
  "nodes": [
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-A"
    },
    {
      "pm_addr": "192.100.0.6",
      "name": "TB1-OSC-B"
    },
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-D"
    },
    {
      "pm_addr": "192.100.0.7",
      "name": "TB1-OSC-C"
    }
  ]
}

I am using jq version : 1.3 and I have no way to upgade it. I tried using add and =+ parameters provided by jq. But it did not help.

Thanks in advnace

Upvotes: 0

Views: 721

Answers (2)

peak
peak

Reputation: 116967

The key to a clear and maintainable solution is having suitable utility functions.

Utility functions for jq 1.3

def insert_at(i; x): .[:i] + [x] + .[i:];

def indicesof(f):
  . as $in
  | range(0;length) | select( $in[.] | f );

def insert_after(f; x):
  [indicesof(f)] as $ix
  | if ($ix|length) > 0
    then insert_at($ix[0]+1; x) 
    else . end;

Utility functions for jq 1.4 or later

jq 1.4 allows a more efficient solution, courtesy of first/1:

def insert_at($i; $x): .[:$i] + [$x] + .[$i:];

def indicesof(f):
  . as $in
  | range(0;length) | select( $in[.] | f );

def insert_after(f; $x):
  (first(indicesof(f)) // null) as $i
  | if $i then insert_at($i + 1; $x) else . end;

Solution

With these utility functions, solutions to the given problem can be expressed directly. For example, assuming an invocation along the lines of:

jq -s -f merge.jq file1.json file2.json

.[1].nodes[0] as $file2
| .[0]
| .nodes |= insert_after(.name == "TB1-OSC-B"; $file2)

This can easily be modified depending on the detailed requirements.

Upvotes: 2

Thor
Thor

Reputation: 47219

If you wanted to append the object from file2.json to the nodes array it is fairly straightforward:

jq -s '.[0].nodes += [ .[1].nodes[0] ] | .[0]' file1.json file2.json

Output:

{
  "nodes": [
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-A"
    },
    {
      "pm_addr": "192.100.0.6",
      "name": "TB1-OSC-B"
    },
    {
      "pm_addr": "192.100.0.7",
      "name": "TB1-OSC-C"
    },
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-D"
    }
  ]
}

However, because you want to insert the object, it becomes more tricky, as jq AFAIK does not support insertion into arrays, only prepending and appending.

Below is a rather convoluted example of how to do this by splicing it in:

parse.jq

# Find the desired index and store it as $n
(.[0].nodes | map(.name == "TB1-OSC-B") | index(true)) as $n   |

# Splice the object from the second file  into the first
.[0].nodes =   .[0].nodes[0:($n|tonumber+1)]   +
             [ .[1].nodes[0] ]                 +
               .[0].nodes[($n|tonumber+1):(.[0].nodes|length)] |

# Only output the now modified object from the first file 
.[0]

Run it like this:

jq -sf parse.jq file1.json file2.json

Output in this case:

{
  "nodes": [
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-A"
    },
    {
      "pm_addr": "192.100.0.6",
      "name": "TB1-OSC-B"
    },
    {
      "pm_addr": "192.100.0.4",
      "name": "TB1-OSC-D"
    },
    {
      "pm_addr": "192.100.0.7",
      "name": "TB1-OSC-C"
    }
  ]
}

Upvotes: 2

Related Questions