jq produces `is not defined at <top-level>` error - bash

I'm seeing a is not defined at <top-level> when calling jq like so:
jq ".Changes[0].ResourceRecordSet.Name = word-is-here.domain.com" someFile.json
The error repeats for each word separated by a dash in the second side of the replacement. The full error is like
jq: error: word/0 is not defined at <top-level>, line 1:
.Changes[0].ResourceRecordSet.Name = word-is-here.domain.com
I've tried escaping quotes in many different ways but that didn't help. (what I mean by this is doing "'"'" weird stuff, I'm still learning bash so I'm just trowing stuff at the wall until it sticks)
EDIT:
So I'm trying to run this in a bash script, and both side of the = signs are variables such as jq --arg value "$value" --arg key "$key" '$key = $value' "$path" (what I tried after a suggestion)
and got the error:
Invalid path expression with result ".Changes[0].ResourceRecor...
The json I'm using is as such:
{
"Changes": [
{
"Action": "do something",
"ResourceRecordSet": {
"Name": "some name here to replace",
...
}
}
]
}

jq '.Changes[0].ResourceRecordSet.Name = "word-is-here.domain.com"' file.json
Quote the string you are assigning. Or pass it to jq via an argument:
jq --arg foo 'words-here' '.Changes[0].ResourceRecordSet.Name = $foo' file.json
For passing the path to the key you want as an argument, a suggestion from https://github.com/stedolan/jq/issues/1493 might work:
jq --argjson path '["Changes",0,"ResourceRecordSet","Name"]' \
--arg val 'word-is-here.domain.com' \
'getpath($path) = $val' file.json

The problem (or at least the obvious problem) here is evidently the string: word-is-here.domain.com, since jq is interpreting the dash ("-") as an operation ("minus").
Unfortunately, since you haven't given us many clues, it's not completely clear what specifically needs to be changed, but a reasonable guess is that word-is-here.domain.com is intended as a fixed string. If so, you would have to present it as a JSON string. So in a bash or bash-like environment, you could write:
jq '.Changes[0].ResourceRecordSet.Name = "word-is-here.domain.com"' someFile.json
Specifying the LHS path via a shell variable
If the LHS path must be specified by a shell variable, it should if possible be passed in as a JSON array, e.g. using the --argjson command-line option; one can then use an expression of the form setpath($path; $value) to update the path.
If for some reason a solution allowing the LHS to be specified as a jq path is preferred, then shell string-interpolation could be used, though as with any such interpolation, this should be done with care.

Related

jq: Append JSON object via shell variable

I have a JSON file and I'm trying to add another field to it.
Example JSON File:
{"data":{}}
Looking at other answers the += seems to work:
objectName="objName"
cat $jsonFile | jq --arg objName $objectName '.data[$objName] += {"test": "json"}'
Outputs
{
"data": {
"objName": {
"test": "json"
}
}
}
just as expected.
The problem is that I can't hardcode the JSON so I'm inputting the string as a variable. But I can't get the syntax to work:
objectName="objName"
objJSON='{"test": "json"}'
cat $jsonFile | jq -r --arg objName $objectName --arg jsonString $objJSON '.data[$objName] += $jsonString'
I'm getting the error jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shell quoting issues?) at <top-level>, line 1: "json"}
Use --argjson rather than --arg when passing JSON rather than strings.
When in doubt, quote your strings; that way if the shell executing your code isn't the one you think it is, you won't have your values being munged. (It's also helpful to stay in the habits necessary to write portable code; the whole reason I dropped zsh after spending 6 months learning it back in the mid-2000s is that my code quality when writing for other shells suffered).
objName="objName"
objJSON='{"test": "json"}'
echo '{"data":{}}' |
jq --arg objectName "$objName" \
--argjson jsonString "$objJSON" \
'.data[$objectName] += $jsonString'
...properly emits:
{
"data": {
"objName": {
"test": "json"
}
}
}

How do I concatenate dummy values in JQ based on field value, and then CSV-aggregate these concatenations?

In my bash script, when I run the following jq against my curl result:
curl -u someKey:someSecret someURL 2>/dev/null | jq -r '.schema' | jq -r -c '.fields'
I get back a JSON array as follows:
[{"name":"id","type":"int","doc":"Documentation for the id field."},{"name":"test_string","type":"string","doc":"Documentation for the test_string field"}]
My goal is to do a call with jq applied to return the following (given the example above):
{"id":1234567890,"test_string":"xxxxxxxxxx"}
NB: I am trying to automatically generate templated values that match the "schema" JSON shown above.
So just to clarify, that is:
all array objects (there could be more than 2 shown above) returned in a single comma-delimited row
doc fields are ignored
the values for "name" (including their surrounding double-quotes) are concatenated with either:
:1234567890 ...when the "type" for that object is "int"
":xxxxxxxxxx" ...when the "type" for that object is "string"
NB: these will be the only types we ever get for now
Can someone show me how I can expand upon my initial jq to return this?
NB: I tried working down the following path but am failing beyond this...
curl -u someKey:someSecret someURL 2>/dev/null | jq -r '.schema' | jq -r -c '.fields' | "\(.name):xxxxxxxxxxx"'
If it's not possible in pure JQ (my preference) I'm also happy for a solution that mixes in a bit of sed/awk magic :)
Cheers,
Stan
Given the JSON shown, you could add the following to your pipeline:
jq -c 'map({(.name): (if .type == "int" then 1234567890 else "xxxxxxxxxx" end)})|add'
With that JSON, the output would be:
{"id":1234567890,"test_string":"xxxxxxxxxx"}
However, it would be far better if you combined the three calls to jq into one.

Passing Environment variables to a jq command [duplicate]

I am trying to use jq to construct a hash in which a key name comes from a variable. Something like this:
jq --null-input --arg key foobar '{$key: "value"}'
This doesn't work, however, and gives the following error:
error: syntax error, unexpected '$'
{$key: "value"} 1 compile error
Use parentheses to evaluate $key early as in:
jq --null-input --arg key foobar '{($key): "value"}'
See also: Parentheses in JQ for .key
You can also use String interpolation in jq which is of the form "\(..)". Inside the string, you can put an expression inside parens after a backslash. Whatever the expression returns will be interpolated into the string.
You can do below. The contents of the variable key is expanded and returned as a string by the interpolation sequence.
jq --null-input --arg key foobar '{ "\($key)": "value"}'

How to make jq treat argument as numeric instead of string?

How to make jq treat an input argument as numeric instead of string? In the following example, CURR_INDEX is a Bash variable which has array index value that I want to extract.
jq --arg ARG1 $CURR_INDEX '.[$ARG1].patchSets' inputfile.json
I get the following error:
jq: error: Cannot index array with string
I tried the workaround of using bash eval but some jq filters do not work properly in eval statements.
You can convert it to a number, like this:
jq --arg ARG1 1 '.[$ARG1|tonumber]' <<< '["foo". "bar"]'
"bar"
--arg always binds the value as a string. You can use --argjson (introduced in version 1.5) to treat the argument as a json-encoded value instead.
jq --argjson ARG1 $CURR_INDEX '.[$ARG1].patchSets' inputfile.json
To see it in action, you can reproduce your original error:
$ jq --argjson ARG1 '"1"' '.[$ARG1]' <<< '["foo", "bar"]'
jq: error (at <stdin>:1): Cannot index array with string "1"
then correct it:
$ jq --argjson ARG1 1 '.[$ARG1]' <<< '["foo", "bar"]'
"bar"

How to create for-loops with jq in bash

I'm trying to split a json file into various json files. The input (r1.json) looks like :
{
"results" : [
{
content 1
}
,
{
content 2
}
,
{
content n
}
]
}
I'd like the output to be n files : 1.json, 2.json, n.json. Respectively containing {content 1}, {content 2} and {content n}.
I tried :
for i in {0..24}; do cat r1.json | jq '.results[$i]' >> $i.json; done
But I have the following error: error: i is not defined
While the above answers are correct, note that interpolating shell variables in jq scripts is a terrible idea for all but the most trivial of scripts. On any of the solutions provided, replace the following:
jq ".results[$i]"
With the following:
jq --arg i "$i" '.results[$i | tonumber]'
Try
for i in {0..24}; do cat r1.json | jq ".results[$i]" >> $i.json; done
Note that shell variables can't be expanded inside of single-quotes.
IHTH
The single quotes are probably what is messing you up. Bash variables are not expanded in single quotes. You are passing a literal string .results[$i] to jq. Try double quotes instead:
for i in {0..24}; do
cat r1.json | jq ".results[$i]" >> $i.json
done

Resources