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.
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.
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 |= [ _ ] + .
jq has a flag for feeding actual JSON contents with its --argjson flag. What you need to do is, store the content of the first JSON file in a variable in jq's context and update it in the second JSON
jq --argjson groupInfo "$(<input.json)" '.[].groups += [$groupInfo]' orig.json
The part "$(<input.json)" is shell re-direction construct to output the contents of the file given and with the argument to --argjson it is stored in the variable groupInfo. Now you add it to the groups array in the actual filter part.
Putting it in another way, the above solution is equivalent of doing this
jq --argjson groupInfo '{"id": 9,"version": 0,"lastUpdTs": 1532371267968,"name": "Training" }' \
'.[].groups += [$groupInfo]' orig.json
This is the exact case that the input function is for:
inputand inputs [...] read from the same sources (e.g., stdin, files named on the command-line) as jq itself. These two builtins, and jq’s own reading actions, can be interleaved with each other.
That is, jq reads an object/value in from the file and executes the pipeline on it, and anywhere input appears the next input is read in and is used as the result of the function.
That means you can do:
jq '.[].groups += [input]' orig.json input.json
with exactly the command you've written already, plus input as the value. The input expression will evaluate to the (first) object read from the next file in the argument list, in this case the entire contents of input.json.
If you have multiple items to insert you can use inputs instead with the same meaning. It will apply across a single or multiple files from the command line equally, and [inputs] represents all the file bodies as an array.
It's also possible to interleave things to process multiple orig files, each with one companion file inserted, but separating the outputs would be a hassle.
jq append data and keep the rest - Unix & Linux Stack Exchange
Using jq to add/prepend an element to the top of an array - Stack Overflow
Adding unique item to array
bash - How do I use jq to append an array to this json string? - Stack Overflow
Well, does something like this work for you?
jq '.[0].array += ["string3"]' test.json
I see this response, which looks pretty much like what I would want:
[
{
"array": [
"string1",
"string2",
"string3"
]
},
{
"object1": {
"subobject1": {},
"subobject2": {}
}
},
{
"object2": {}
}
]
EDIT 1: I like the more general solutions provided by @peak particularly the first one:
map( if .array then .array += ["string3"] else . end)
Here's a snippet you can experiment with. It's easy to see how you could insert the new string into the second contained array rather than the first with:
map( if .array2 then .array2 += ["string3"] else . end)
Depending on your requirements, you may wish to consider:
map( if .array then .array += ["string3"] else . end)
or even:
walk( if type == "object" and .array then .array += ["string3"] else . end)
If you only want the first occurrence at the top-level to be affected:
(first(.[]|.array // empty)) += ["string3"]
How to you approach this?
There are several issues with your script. Generally, this should work (but still needs improvement on certain levels):
JSON='[{"name": "Jon", "class": "senior"}]'
echo "$JSON" | jq '. += [{"new_key": "new_value"}]'
[
{
"name": "Jon",
"class": "senior"
},
{
"new_key": "new_value"
}
]
This question is likely a duplicate of Add new element to existing JSON array with jq.
Anyway as a shortcut, here is how you do it without piping the source JSON array:
JSON='[{"name": "Jon", "class": "senior"}]'
jq --null-input --argjson a "$JSON" '$a + [{"name" : "santa", "class" : "christmas" }]'
[THIS IS NOT WORKING. READ BELOW.]
There are many ways to do this.
Assuming elements of the array are supposed to be unique, which your use case strongly implies, you can just pass the resulting array after the addition through the unique filter.
$ cat foo.json | jq '.widgets[] | select(.name=="foo").properties |= (.+ ["cat"] | unique)'
There are a few problems here.
One is that the resulting output is partial as it is missing the container object.
Another one is that the edited array looses the commas separating the objects thus becoming illegal JSON.
The actual result from the above command is:
{
"name": "foo",
"properties": [
"baz",
"cat"
]
}
{
"name": "bar"
}
There's more than one way to skin a cat, as they say, but perhaps this will give you some ideas:
.widgets[]
| select(.name=="foo")
| select(.properties | index("cat") | not)
| .properties += ["cat"]
With your input, the result is:
{
"name": "foo",
"properties": [
"baz",
"cat"
]
}
The following may be closer to what you're looking for:
.widgets |= [ .[] | if .properties|index("cat")|not
then .properties += ["cat"]
else .
end]