Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Numeric argument passed with jq --arg not matching data with ==

Here is a sample JSON response from my curl:

{
  "success": true,
  "message": "jobStatus",
  "jobStatus": [
    {
      "ID": 9,
      "status": "Successful"
    },
    {
      "ID": 2,
      "status": "Successful"
    },
   {
      "ID": 99,
      "status": "Failed"
    }
  ]
}

I want to check the status of ID=2. Here is the command I tried:

cat test.txt|jq --arg v "2" '.jobStatus[]|select(.ID == $v)|.status'

response: there is none

I tried value 2 without quotes and still no result.

By contrast, if I try the command with a literal 2, it works:

cat test.txt | jq '.jobStatus[]|select(.ID == 2)|.status'

response:

"Successful"

I'm stuck. Can anyone help me identify the problem?

like image 341
user1207929 Avatar asked Jan 20 '17 21:01

user1207929


1 Answers

jq is data-type-aware:

  • .ID, as defined in the JSON input, is a number,

  • but any command-line argument passed with --arg (such as v here) is invariably a string (whether you quote the value or not),

so, in order to compare them, you must use an explicit type conversion, such as with tonumber/1:

jq --arg v '2' '.jobStatus[] | select(.ID == ($v | tonumber)) | .status' test.txt

Given that you're only passing a scalar argument here, the following solution, using --argjson (jq v1.5+) is a bit of an overkill, but it is an alternative to explicit type conversion in that passing a JSON argument in effect passes typed data:

jq --argjson v '{ "ID": 2 }' '.jobStatus[] | select(.ID == $v.ID) | .status' test.txt

peak's answer demonstrates that even --argjson v 2 works (in which case comparing to $v works directly), which is certainly the most concise solution, but may require an explanation:

  • Even though 2 may not look like JSON, it is: it is a valid JSON text containing a single value of type number (see json.org).

    • Specifically, it is the fact that 2 is an unquoted token that starts with a digit that makes it a number in the context of JSON (the JSON string-value equivalent is "2", which from the shell would have to be passed as '"2"' - note the embedded double quotes).
  • Therefore jq interprets --argjson -v 2 as a number, and comparison .ID == $v works as intended (note that the same applies to --argjson -v '2' / --argjson -v "2", where the shell removes the quotes before jq sees the value).
    By contrast, anything you pass with --arg is always a string value that is used as-is.

  • In other words: --argjson, whose purpose is to accept arbitrary JSON texts as strings (such as '{ "ID": 2 }' in the example above), can also be used to pass number-string scalars to force their interpretation as numbers.
    The same technique also works with Boolean strings true and false.


Tip of the hat to peak for his help.

like image 61
mklement0 Avatar answered Oct 12 '22 01:10

mklement0