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

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"

Related

how can I query this hash output in bash? [duplicate]

I'm trying to get jq to parse a JSON structure like:
{
"a" : 1,
"b" : 2,
"c" : "{\"id\":\"9ee ...\",\"parent\":\"abc...\"}\n"
}
That is, an element in the JSON is a string with escaped json.
So, I have something along the lines of
$ jq [.c] myFile.json | jq [.id]
But that crashes with jq: error: Cannot index string with string
This is because the output of .c is a string, not more JSON.
How do I get jq to parse this string?
My initial solution is to use sed to replace all the escape chars (\":\", \",\" and \") but that's messy, I assume there's a way built into jq to do this?
Thanks!
edit:
Also, the jq version available here is:
$ jq --version
jq version 1.3
I guess I could update it if required.
jq has the fromjson builtin for this:
jq '.c | fromjson | .id' myFile.json
fromjson was added in version 1.4.
You can use the raw output (-r) that will unescape characters:
jq -r .c myfile.json | jq .id
ADDENDUM: This has the advantage that it works in jq 1.3 and up; indeed, it should work in every version of jq that has the -r option.
Motivation: you want to parse JSON string - you want to escape a JSON object that's wrapped with quotes and represented as a String buffer, and convert it to a valid JSON object. For example:
some JSON unescaped string :
"{\"name\":\"John Doe\",\"position\":\"developer\"}"
the expected result ( a JSON object ):
{"name":"John Doe","position":"developer"}
Solution: In order to escape a JSON string and convert it into a valid JSON object use the sed tool in command line and use regex expressions to remove/replace specific characters:
cat current_json.txt | sed -e 's/\\\"/\"/g' -e 's/^.//g' -e 's/.$//g'
s/\\\"/\"/g replacing all backslashes and quotes ( \" ) into quotes only (")
s/^.//g replacing the first character in the stream to none character
s/.$//g replacing the last character in the stream to none character

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

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.

How to use key variables to get a second level value of json file using jq

I need to get a specific value from a json file using jq in a bash script (busybox). The json file looks like this:
{
"example.com": {
"backend": "1.3.7"
}
}
In my script there are two variables: project and app - as there are of course multiple projects and applications. I need to use these variable values to get the version value. In this example project is "example.com" and app is "backend"
I tried this
jq --arg p "$project" --arg a "$app" '.[$p].[$a]' file.json
But I do get the error
jq: error: syntax error, unexpected '[', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.[$p].[$a]
jq: 1 compile error
You may use it like this:
jq -r --arg p "$project" --arg a "$app" '.[$p][$a]' file.json
or else:
jq -r --arg p "$project" --arg a "$app" '.[$p] | .[$a]' file.json
1.3.7

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 build json without escaping new line?

I run jq from bash and all my new lines are escaped
release_message="\`\`\`a\na\n\`\`\`"
query=$(jq -n \
--arg message $release_message \
"{text:\$message}"
);
echo "query $query"
result
query {
"text": "```a\\na\\n```"
}
How to prevent extra escape from jq?
You can either encode your input in JSON yourself or let jq do it
option 1 : encode yourself
release_message='"```a\na\n```"'
jq -n --argjson message "$release_message" '{text:$message}'
# or :
# echo "$release_message" | jq '{text:.}'
Have bash produce a valid JSON string (note : quotes-enclosed), pass through the standard input or with --argjson.
option 2 : let jq encode the string
release_message='```a
a
```'
jq --arg message "$release_message" '{text:$message}'
# or :
# echo "$release_message" | jq -R --slurp '{text:.}'
Have bash produce the literal string, pass with --arg or specify --raw-input/-R to have the input encoded in JSON, plus --slurp so that the multiple lines are considered as a single string.
Since you're using bash, it's generally best to use single quotes unless you want string interpolation. Consider, for example:
release_message='```a\na\n```'
query=$(jq -n \
--arg message "$release_message" \
'{text: $message }'
);
echo "query $query"
You might want to consider using $'....':
release_message=$'```a\na\n```'
Using gsub
Depending on what your actual goals are, you might want to use gsub, e.g.
release_message='```a\na\n```'
query=$(jq -n \
--arg message "$release_message" \
'{text: ($message | gsub("\\n";"\n")) }'
);
echo "query $query"
produces:
query {
"text": "```a\\na\\n```"
}

Resources