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 - add objects from file into json array - Unix & Linux Stack Exchange
jq append data and keep the rest - Unix & Linux Stack Exchange
bash - Adding object string to array using jq doesn't translate escape characters - Unix & Linux Stack Exchange
Using jq to add/prepend an element to the top of an array - Stack Overflow
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.
I've found the answer:
jq --arg ITEM "$block" '.+[$ITEM|fromjson]' <<< "$parent"
or
jq --argjson ITEM "$block" '.+[$ITEM]' <<< "$parent"
The issue is twofold:
You are handing JSON to
jqusing--arg, which expects text. This means thatjqwill JSON-encode the given text into a string. Thejqutility has a separate option,--argjson, for when the passed data is a JSON document.In the most general case, you don't want to inject shell variables into a JSON document without encoding their contents. You do this by passing the variable's data to
jqusing--arg, or by using some other tool (likejo) which expects non-JSON input and produces JSON output.
- You also use
[]unquoted, which would cause the shell to complain if it has thefailglob(or equivalent, likenomatchinzsh) shell option set. Thezshshell has itsnomatchshell option set by default.
Your issue would be best solved by using jq to add the data directly to your existing document:
jq \
--arg block_id "$block_id" \
--arg block_parent_id "$block_parent_id" \
--argjson current_index 0 \
--argjson child_sum 0 \
--arg block_cidr "" \
--arg block_size "" \
--arg child_cidr "" \
--arg child_size "" \
'. += [ $ARGS.named | .subnets = $ARGS.positional ]' \
--args \
<<<"$parent"
In the above command, I create the object that I add to the exsisting list in $parent by using $ARGS.named for each plain (scalar) key-value pair. The value of $ARGS.named is an object with the keys and values taken from the --arg options and their values. I then add the subnets array by using $ARGS.positional. This value is the array of values after --args on the command line. The --args option is required to be the last option on the command line, and the list is empty in the command above.
If you want to create the object that you add separately:
to_add=$(
jq -n \
--arg block_id "$block_id" \
--arg block_parent_id "$block_parent_id" \
--argjson current_index 0 \
--argjson child_sum 0 \
--arg block_cidr "" \
--arg block_size "" \
--arg child_cidr "" \
--arg child_size "" \
'[ $ARGS.named | .subnets = $ARGS.positional ]' \
--args
)
jq --argjson block "$to_add" '. += $block' <<<"$parent"