简体   繁体   中英

How do I make this jq expression, which works in jq >= 1.5, backwards-compatible / correct for versions < 1.5

In any version (that i've tried) of jq >= 1.5 , this works fine .
In any version of jq <= 1.4 , I get the following error:

error: syntax error, unexpected as, expecting FORMAT or QQSTRING_START

There's a lot of information around on the error: "expecting FORMAT or QQSTRING_START" , so far what I've seen has not applied here, I wonder if it's the as that I am not implementing reliably

I can recreate the problem with this trivial code: (can be copied/pasted as is to test)

jq \
'(..|.liveBroadcastContent?'\
'| foreach . as $item (.; if $item == null then false else . end)) as $chanstatus '\
'| {location:"location",
channel:"channelid",
current_id:"vId",
id_status:$chanstatus,
url:"https://youtu.be/\("vId")"}' \
<<<'{"liveBroadcastContent":true}'

Desired result achieved on jq > 1.5 :

{
  "location": "location",
  "channel": "channelid",
  "current_id": "vId",
  "id_status": true,
  "url": "https://youtu.be/vId"
}

What it's supposed to do:

  • If liveBroadcastContent exists in the input: then output it's value in the id_status key
  • If liveBroadcastContent does not exist : then output false in the id_status key
  • Output everything else as written

Actual Input :

This is what goes into the program when there is a live stream present.
This is what goes into the program when there is no live streams

Hence I thought ..|.liveBroadcastContent? was the most reliable way to basically say " If this key appears somewhere in the data, please return it's value in <this> part of my fixed-format output data "

Now that the application is obvious, please note that I am aware of using other methods such as checking the value of "totalResults" , in combination with HTTP 200 , amongst others, however:

  1. This application of jq may be useful elsewhere and I'd like to address it as it.
  2. This whole program is a shared program invoked as a one-shot on demand basis, which is a backup method (API key) of a more comprehensively implemented oauth server application. On occasion the input data is different, thus I just want to recursively search for liveBroadcastContent

EDIT: Just to clarify - this isn't about the behavior of jq across versions (something that would belong in the jq forums) - It's about making the most robust portable code. I tend to think that versions >1.5 are simply forgiving a poorly formed expression (which I'm trying to work out here)

  1. The reason your snippet fails in jq 1.4 is that foreach was only introduced in version 1.5.

  2. I'm not sure that your program with foreach is consistent with your stated requirements, which unfortunately are slightly unclear. For example, what if the "liveBroadcastContent" key appears more than once in the input JSON entity?

  3. Your stated requirements as I understand them (glossing over the ambiguity) would correspond to the following program, which has been tested with jq 1.4, 1.5 and the current "master" version:

 (reduce (..| select(type == "object" and has("liveBroadcastContent"))) as $item
    (false; $item | .liveBroadcastContent)) as $chanstatus
 | {location:"location",
    channel:"channelid",
    current_id:"vId",
    id_status:$chanstatus,
    url:"https://youtu.be/\("vId")"}
  1. For multiline programs such as this, it's usually much more convenient to use the -f option of jq, eg jq -f program.jq

  2. If you need to accommodate jq 1.3 as well, then you'd have to handle .. , which can be done by using dotdot defined as follows:

def dotdot:
  recurse(if (type | . == "object" or . == "array") then .[] else empty end);

I'm not sure what your input actually looks like, but given what you've shown here, it doesn't have to be that complicated.

{
    location: "location",
    channel: "channelid",
    current_id: "vId",
    id_status: (.liveBroadcastContent? // false),
    url: "https://youtu.be/vId"
}

You wanted to get the value of liveBroadcastContent if it exists, otherwise false and that's what precisely .liveBroadcastContent? // false .liveBroadcastContent? // false does (assuming null is not a valid value).


Seeing your full input object, it seems like a search result from a web api. I don't think using recursion here is really necessary. If the goal is take each of the results and display various information about each one, I would write this filter instead. (guessing at some of the fields)

.items[] | {
    location: .channelTitle,
    channel: .channelId,
    current_id: .id,
    id_status: (.liveBroadcastContent? // false),
    url: "https://youtu.be/\(.id)"
}

If on the other hand, you were using the search results to determine whether a feed was available (based on your negative input case), I think it would be better to approach it finding the property on the list of results, rather than just the mere existence of the property anywhere.

I would do this instead:

{
    location: "location",
    channel: "channelid",
    current_id: "vId",
    id_status: ([.items[].snippet.liveBroadcastContent | select(. != null)][0] // false),
    url: "https://youtu.be/vId"
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM