Add new element to existing JSON array with jq

117,182

Solution 1

The |= .+ part in the filter adds a new element to the existing array. You can use jq with filter like:

jq '.data.messages[3] |= . + {"date": "2010-01-07T19:55:99.999Z", "xml": "xml_samplesheet_2017_01_07_run_09.xml", "status": "OKKK", "message": "metadata loaded into iRODS successfullyyyyy"}' inputJson

To avoid using the hardcoded length value 3 and dynamically add a new element, use . | length which returns the length, which can be used as the next array index, i.e.,

jq '.data.messages[.data.messages| length] |= . + {"date": "2010-01-07T19:55:99.999Z", "xml": "xml_samplesheet_2017_01_07_run_09.xml", "status": "OKKK", "message": "metadata loaded into iRODS successfullyyyyy"}' inputJson

(or) as per peak's suggestion in the comments, using the += operator alone

jq '.data.messages += [{"date": "2010-01-07T19:55:99.999Z", "xml": "xml_samplesheet_2017_01_07_run_09.xml", "status": "OKKK", "message": "metadata loaded into iRODS successfullyyyyy"}]'

which produces the output you need:

{
  "report": "1.0",
  "data": {
    "date": "2010-01-07",
    "messages": [
      {
        "date": "2010-01-07T19:58:42.949Z",
        "xml": "xml_samplesheet_2017_01_07_run_09.xml",
        "status": "OK",
        "message": "metadata loaded into iRODS successfully"
      },
      {
        "date": "2010-01-07T20:22:46.949Z",
        "xml": "xml_samplesheet_2017_01_07_run_09.xml",
        "status": "NOK",
        "message": "metadata duplicated into iRODS"
      },
      {
        "date": "2010-01-07T22:11:55.949Z",
        "xml": "xml_samplesheet_2017_01_07_run_09.xml",
        "status": "NOK",
        "message": "metadata was not validated by XSD schema"
      },
      {
        "date": "2010-01-07T19:55:99.999Z",
        "xml": "xml_samplesheet_2017_01_07_run_09.xml",
        "status": "OKKK",
        "message": "metadata loaded into iRODS successfullyyyyy"
      }
    ]
  }
}

Use jq-play to dry-run your jq-filter and optimize any way you want.

Solution 2

Rather than using |=, consider using +=:

.data.messages += [{"date": "2010-01-07T19:55:99.999Z",
   "xml": "xml_samplesheet_2017_01_07_run_09.xml",
   "status": "OKKK", "message": "metadata loaded into iRODS successfullyyyyy"}]

Prepend

On the other hand, if (as @NicHuang asked) you want to add the JSON object to the beginning of the array, you could use the pattern:

 .data.messages |= [ _ ] + .

Solution 3

Summary: ". +" is your saviour

Details:

For adding an entry to a list: You can append [list1] + [list2] (and not [list] + data)

$ echo '[ "data1" ]' | jq '. + [ "data2" ]'
[
  "data1",
  "data2"
]

$ echo '[ {"key1": "value1"} ]' | jq '. + [{"key2": "value2"}]'
[
  {
    "key1": "value1"
  },
  {
    "key2": "value2"
  }
]

For adding a key/value to a dictionary:

$ echo '{"key1": "value1"}' | jq '. + {"key2": "value2"}'
{
  "key1": "value1",
  "key2": "value2"
}

References:

https://gist.github.com/joar/776b7d176196592ed5d8

Share:
117,182
Felipe
Author by

Felipe

I do this in my "free-time", not as a representative of any company or my employer. All views and opinions expressed are my own. I believe that helping others to solve their problems I can learn twice. Moreover, if we have a problem in common, this might be a good path to grow together.

Updated on February 18, 2022

Comments

  • Felipe
    Felipe about 2 years

    I want to append an element to an array in a JSON file using the jq``add command, but it's not working.

    report-2017-01-07.json file:

    {  
       "report": "1.0",
       "data": {  
          "date": "2010-01-07",
          "messages": [  
             {  
                "date": "2010-01-07T19:58:42.949Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "OK",
                "message": "metadata loaded into iRODS successfully"
             },
             {  
                "date": "2010-01-07T20:22:46.949Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "NOK",
                "message": "metadata duplicated into iRODS"
             },
             {  
                "date": "2010-01-07T22:11:55.949Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "NOK",
                "message": "metadata was not validated by XSD schema"
             }
          ]
       }
    }
    

    I am using this command:

    $ cat report-2017-01-07.json 
    | jq -s '.data.messages {"date": "2010-01-07T19:55:99.999Z", "xml": "xml_samplesheet_2017_01_07_run_09.xml", "status": "OKKK", "message": "metadata loaded into iRODS successfullyyyyy"}'
    jq: error: syntax error, unexpected '{', expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
    .data.messages {"date": "2010-01-07T19:55:99.999Z", "xml": "xml_samplesheet_2017_01_07_run_09.xml", "status": "OKKK", "message": "metadata loaded into iRODS successfullyyyyy"}               
    jq: 1 compile error
    

    Here's how I want the output to look:

    {
        "report": "1.0",
        "data": {
            "date": "2010-01-07",
            "messages": [{
                "date": "2010-01-07T19:58:42.949Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "OK",
                "message": "metadata loaded into iRODS successfully"
            }, {
                "date": "2010-01-07T20:22:46.949Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "NOK",
                "message": "metadata duplicated into iRODS"
            }, {
                "date": "2010-01-07T22:11:55.949Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "NOK",
                "message": "metadata was not validated by XSD schema"
            }, {
                "date": "2010-01-07T19:55:99.999Z",
                "xml": "xml_samplesheet_2017_01_07_run_09.xml",
                "status": "OKKKKKKK",
                "message": "metadata loaded into iRODS successfullyyyyy"
            }]
        }
    }
    
  • peak
    peak about 7 years
    Since the goal is just to append an element, it would be better to use += but it is worth noting that .data.messages[3] |= . + X can here be simplified to .data.messages[3] = X as the . on the RHS is effectively just null.
  • script'n'code
    script'n'code almost 7 years
    (windows JQ user here): I wonder if it is possible to write this new data to the same (input) file, instead of creating a temporary file which needs to be renamed afterwords..
  • peak
    peak over 6 years
    @script'n'code - Using sponge is probably still the best option if you have it or can install it (it's part of moreutils).
  • Nic Huang
    Nic Huang about 6 years
    @peak how can I insert that object to the beginning of the array, couldn’t figure out . Thanks
  • Harry Ma
    Harry Ma over 4 years
    @peak how can I specify the order when inserting the element to the array ?
  • Vitalii Zinchenko
    Vitalii Zinchenko almost 3 years
    Prepend example: echo '{"arr": [ "data1" ]}' | jq '.arr |= ["data2"] + .' => {"arr": ["data2", "data1"]}