How to get value of numeric object property via jq? - json

I have a json object with numeric keys in an example.json file:
{
"1": "foo",
"2": "bar"
}
I want to get its properties by key via jq, and I have tried:
$ jq ."1" example.json
0.1
and
jq .["1"] example.json
jq: error (at example.json:4): Cannot index object with number
The result should be
"foo"
though.

The command:
jq ."1" example.json
doesn't work because the quotes are interpreted by the shell and the first argument that jq receives is .1. The command above is identical to jq .1 example.json and it is not correct as jq reports.
You need to enclose the jq program in apostrophes to prevent the shell interpret any character in it:
jq '."1"' example.json
This way, jq receives ."1" as its program and happily interprets it.
You can also put the key name into square brackets (as you have already tried) but it doesn't add any improvement, it's the same program only bloated. And it gives you more reasons to put it into apostrophes to protect it from the shell:
jq '.["1"]' example.json

Use quotes:
$ jq '."1"' example.json
"foo"

Related

Parse JSON key starting in % with jq [duplicate]

This question already has answers here:
How to use jq when the variable has reserved characters?
(3 answers)
Closed last month.
Assume the following output from a REST API (stored as test.json here):
{
"system": {
"power": 10.5,
"%mem": 0.5,
"%cpu": 12.4
}
}
I can easily query the first item:
$ jq '.system.power' test.json
10.5
However:
$ jq '.system.%cpu' test.json
jq: error: syntax error, unexpected '%', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.system.%cpu
jq: 1 compile error
According to JSON.org, a string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. Any valid string can be used as a JSON key. So %cpu seems indeed valid and, indeed, jq can handle this elsewhere just fine:
$ jq '.system | has("%cpu")' test.json
true
Question: How can I extract the lower two properties from the system object?
JSON keys are always strings, but jq sometimes allows to use them without string (if they would be a valid "identifier"). You must quote it if it contains special characters such as %:
jq '.system."%cpu"' test.json
or jq '.system["%cpu"]' test.json

Select multiple values from a split string in JQ

I see questions about selecting multiple values from an array using JQ, but I have a string that originally I just need the value after the last /, which is easily selected:
Input:
https://www.googleapis.com/compute/v1/projects/test-project-1/zones/europe-west1-b/instanceGroups/test-instance-group-1
JQ:
jq -r '.[]|.zone|=split("/")[-1]|"\(.name) \(.zone)"'
Output:
test-instance-group-1 europe-west1-b
However for the actual instances, the zone isn't listed, so must be extracted from the same key instance.
Input:
https://www.googleapis.com/compute/v1/projects/test-project-1/zones/europe-west1-b/instances/test-instance-1
JQ:
jq -r '.[]|.instance|=split("/")[-1]|"\(.instance)'
Output:
test-instance-1
However, I also want to extract the zone infomation, from the input as well, which I presume is selected with =split("/")[-3] however no matter how I format the request to JQ, I get errors:
$ jq -r '.[]|.instance|=split("/")[-1][-3]|"\(.instance)"'
jq: error (at <stdin>:47): Cannot index string with number
How can I extract two strings, from the same value/key ?
You're looking for something like this:
jq -r '.[].instance | split("/") | "\(.[-1]) \(.[-3])"'

Is there a better solution to cURL this JSON?

I have this function that iterates over the keys of a given JSON using curl and jq as the JSON processor, My code looks like this : (bash)
function getJSONContent {
option=""
for option in "header" "developer" "category" "description"
do
content=$(curl -s $extension_url | jq ".[\"$extension\"][\"$option\"]")
printf "$content\n"
done
}
But the problem is that it curl's 4 time and I haven't found a better solution to this without getting an error.
Is doing this okay? Or is there just a better solution to do this in Bash / Shellscript?
is there just a better solution ... ?
Yes!
You evidently only need one call to curl and one to jq, but at the very
least, you should avoid calling curl more than once.
Avoid constructing the jq command "on the fly". Instead, you can pass in the shell (or environment) variables
on the command line, e.g. using --arg or --argjson
In this specific case, it looks like you can avoid calling jq more than once by simply using jq's ',' operator.
In brief, try something along the following lines:
curl -s "$extension_url" |
jq --arg extension "$extension" '
.[$extension]["header","developer","category","description"]'

jq: slicing array based on argument

I am trying to slice an array in jq where the end index is passed as an argument from the shell (bash):
end_index=7
cat obj.json | jq --arg eidx $end_index, '.arr[0:$eidx]'
This works as expected when the index is hard-coded
cat obj.json | jq '.arr[0:7]'
but in the example at the top, I get an error message
jq: error (at <stdin>:0): Start and end indices of an array slice must be numbers
I suspect this might be to do with how jq handles variable substitution within the slice operator [:], but none of my attempts to solve the problem, e.g. by enclosing the variable name in curly braces .arr[0:${eidx}], has worked.
You can convert a string to a number using tonumber, as in:
jq --arg eidx 1 '.arr[0:($eidx|tonumber)]'
If your jq is sufficiently recent, you can use --argjson instead of --arg:
jq --argjson eidx 1 '.arr[0:$eidx]'
When you pass an argument via --arg it is treated as a string, not an int:
--arg name value:
This option passes a value to the jq program as a predefined variable. If you run jq with --arg foo bar, then $foo is available in the program and has the value "bar". Note that value will be treated as a string, so --arg foo 123 will bind $foo to "123".
From the docs (emphases added)
so it would seem that you cannot use --arg to pass a value to be used in a slice. In this example, you could just use the shell expansion though:
jq ".arr[0:$end_index]" obj.json
the double quotes will have the shell expand your variable before passing it to jq (though other expansions will happen to, so make sure you mean them to happen.

I cannot get jq to give me the value I'm looking for.

I'm trying to use jq to get a value from the JSON that cURL returns.
This is the JSON cURL passes to jq (and, FTR, I want jq to return "VALUE-I-WANT" without the quotation marks):
[
{
"success":{
"username":"VALUE-I-WANT"
}
}
]
I initially tried this:
jq ' . | .success | .username'
and got
jq: error (at <stdin>:0): Cannot index array with string "success"
I then tried a bunch of variations, with no luck.
With a bunch of searching the web, I found this SE entry, and thought it might have been my saviour (spoiler, it wasn't). But it led me to try these:
jq -r '.[].success.username'
jq -r '.[].success'
They didn't return an error, they returned "null". Which may or may not be an improvement.
Can anybody tell me what I'm doing wrong here? And why it's wrong?
You need to pipe the output of .[] into the next filter.
jq -r '.[] | .success.username' tmp.json
tl;dr
# Extract .success.username from ALL array elements.
# .[] enumerates all array elements
# -r produces raw (unquoted) output
jq -r '.[].success.username' file.json
# Extract .success.username only from the 1st array element.
jq -r '.[0].success.username' file.json
Your input is an array, so in order to access its elements you need .[], the array/object-value iterator (as the name suggests, it can also enumerate the properties of an object):
Just . | sends the input (.) array as a whole through the pipeline, and an array only has numerical indices, so the attempt to index (access) it with .success.username fails.
Thus, simply replacing . | with .[] | in your original attempt, combined with -r to get raw (unquoted output), should solve your problem, as shown in chepner's helpful answer.
However, peak points out that since at least jq 1.3 (current as of this writing is jq 1.5) you don't strictly need a pipeline, as demonstrated in the commands at the top.
So the 2nd command in your question should work with your sample input, unless you're using an older version.