benibela / xidel

Command line tool to download and extract data from HTML/XML pages or JSON-APIs, using CSS, XPath 3.0, XQuery 3.0, JSONiq or pattern matching. It can also create new or transformed XML/HTML/JSON documents.
http://www.videlibri.de/xidel.html
GNU General Public License v3.0
674 stars 42 forks source link

[Bug] error-handling in x:request() ignores --silent #72

Closed Reino17 closed 3 years ago

Reino17 commented 3 years ago

This is really a strange bug.

Xidel 0.9.9-7886:

$ xidel -se 'x:request({"url":"https://github.com/benibela/xidel"})/doc//title'
GitHub - benibela/xidel: Command line tool to [...]

$ xidel -se 'x:request({"url":"https://github.com/benibela/xidel","error-handling":"4xx=accept"})/doc//title'
GitHub - benibela/xidel: Command line tool to [...]

Xidel 0.9.9-7908:

$ xidel -se 'x:request({"url":"https://github.com/benibela/xidel"})/doc//title'
GitHub - benibela/xidel: Command line tool to [...]

$ xidel -se 'x:request({"url":"https://github.com/benibela/xidel","error-handling":"4xx=accept"})/doc//title'
**** Retrieving (GET): https://github.com/benibela/xidel ****
GitHub - benibela/xidel: Command line tool to [...]

The moment you add {"error-handling":"4xx=accept"} Xidel puts out status messages, ignoring -s/--silent.

As far as I can tell, it doesn't affect the command-line option --error-handling, only x:request().

benibela commented 3 years ago

Basically, #70 was caused because it got confused whether it should ignore --input-format or not

Then in https://github.com/benibela/xidel/commit/a4f9169ca4ca5807333b27faf1ac93d7528c7a24 I changed it to ignore nothing

That caused 7886 to crash on complex follow like --follow {"data": ... "error-handling":"4xx=accept"} because the follow would include itself and endlessly request the same webpage

So in https://github.com/benibela/xidel/commit/b6fc3250fd4809103a8264ca95130ef7e09d8c36 I changed it to ignore everything

Reino17 commented 3 years ago

I'm sorry, but I don't know Pascal very well. And especially your complex project makes it even harder to understand the source code. For me at least.

So can you be a bit more explicit?

I changed it to ignore everything

I see the changes you made, but what did you change exactly?
And to ignore what exactly?

But more importantly, can you fix this?

benibela commented 3 years ago

I see the changes you made, but what did you change exactly?

The way it merges the options from the command line with the options from a x:request or --follow

And to ignore what exactly?

To ignore (almost) all command line options in x:request

But more importantly, can you fix this?

Should I fix it?

Perhaps I need two lists. 1) command line options that should be ignored there, 2) command line options that should not be ignored

Reino17 commented 3 years ago

To ignore (almost) all command line options in x:request

Why?! What's up with the sudden killing of functionality? I don't understand.

Is it really one way or the other? In order to fix https://github.com/benibela/xidel/issues/70 you have to destroy functionality elsewhere?

I have to admit, I never use --follow myself, but I do use x:request() quite a lot, because within an extraction-query, or in my case in an XQuery function module, it's the only way to do advanced url requests. Obviously I'm not in favor of any x:request() or --follow options being ignored!

benibela commented 3 years ago

Is it really one way or the other? In order to fix #70 you have to destroy functionality elsewhere?

70 showed that it sometimes ignores command line options and sometimes not, and I forgot when

It must ignore some like the recursive follow.

Or in xidel --post xyz -e 'x:request("http://example.org")' or xidel --method POST -e 'x:request("http://example.org")', should it send a GET or POST request in x:request?

Reino17 commented 3 years ago

Good question. If you want my opinion, then I'd say...
Command-line options (like --post, --method and --error-handling) should only affect the direct input (--data=<string>). Options for x:replace() and --follow should work completely independent.

In your example that would mean that --post xyz and --method POST in the absence of an input url would simply be ignored and thus x:request("http://example.org") would default to x:request({"method":"GET","url":"http://example.org"}).

I've uploaded your {a,b,c}.xml to my own domain for testing...

$ xidel -s --error-handling=xxx=accept "https://rwijnsma.home.xs4all.nl/test/a.xml" -e '
  for $x in //@href return x:request({"url":$x,"error-handling":"4xx=abort"})/doc//title
'
BBB
CCC
DDD

--error-handling=xxx=accept should only affect "https://rwijnsma.home.xs4all.nl/test/a.xml" and nothing else.
{"error-handling":"4xx=abort"} should only affect the invocation of x:request() and nothing else.

For each @href x:request() is called to open the url, parse it, and return the title-node. The output is expected.

With this principle in mind it doesn't make sense to me why --allow-repetitions is needed in this case.

$ xidel -s "https://hidemy.name/en/proxy-list/?type=h" -e '
  for $x in //tbody/tr/concat(td[1],":",td[2]) return
  x:request({
    "proxy":$x,
    "method":"HEAD",
    "error-handling":"xxx=accept",
    "url":"https://github.com"
  })/concat($x," - ",headers[1])
'

For each proxy x:request() is called to do a "https://github.com" HEAD request through this proxy and return this proxy together with the HTTP status. At least xidel doesn't crash anymore like it did back then, but only the first proxy is returned, which for me is unexpected.

If command-line options wouldn't affect x:request() (and it options), then the command would return ALL proxies, just like the previous command would return ALL title-nodes.

But hey, this is just my opinion.

benibela commented 3 years ago

Command-line options (like --post, --method and --error-handling) should only affect the direct input (--data=). Options for x:replace() and --follow should work completely independent.

But at the start of the issue, you argued for the opposite

In your example that would mean that --post xyz and --method POST in the absence of an input url would simply >be ignored and thus x:request("http://example.org") would default to >x:request({"method":"GET","url":"http://example.org"}).

Yes, I think they should be ignored

But other options like --allow-repetitions or --follow-level only exists for --follow

--error-handling=xxx=accept should only affect "https://rwijnsma.home.xs4all.nl/test/a.xml" and nothing else. {"error-handling":"4xx=abort"} should only affect the invocation of x:request() and nothing else.

In that example, there are no errors to handle? Has it ever supported setting error-handling in request?

With this principle in mind it doesn't make sense to me why --allow-repetitions is needed in this case.

Perhaps I should just remove --allow-repetitions / set it to true by default

Reino17 commented 3 years ago

Sorry for the delay. My system disk died.

But at the start of the issue, you argued for the opposite

Did I? Where? I don't think so.

But other options like --allow-repetitions or --follow-level only exists for --follow

In xidel --help it's indeed listed as a "Follow option", but it clearly affects every input/connection.

$ xidel -s videlibri-code_hg/programs/internet/xidel/tests/{a,a}.xml -e '//title'
AAA

$ xidel -s videlibri-code_hg/programs/internet/xidel/tests/{a,a}.xml --allow-repetitions -e '//title'
AAA
AAA
$ xidel -se 'for $x in ("a","a") return x:request(x"https://rwijnsma.home.xs4all.nl/test/{$x}.xml")/doc//title'
AAA

$ xidel -s --allow-repetitions -e 'for $x in ("a","a") return x:request(x"https://rwijnsma.home.xs4all.nl/test/{$x}.xml")/doc//title'
AAA
AAA

Perhaps I should just remove --allow-repetitions / set it to true by default

I would be in favor.

In that example, there are no errors to handle?

True, but it was only an example where x:request() should only respect {"error-handling":"4xx=abort"} and not --error-handling=xxx=accept.

Has it ever supported setting error-handling in request?

Haha, are you really asking me? Haven't you tested this yourself at a certain point?

A perfect example which immediately comes to mind is from my own Youtube extractor notes.

https://github.com/Reino17/xivid/blob/c78ce6ade57f68698400c84fc950d5dd9618b76f/xivid_notes.txt#L5328-L5342

$ xidel -s --xquery '
  for $x in x:request({
    "headers":"Content-Type: application/json",
    "post":serialize(
      {
        "context":{"client":{"clientName":"WEB","clientVersion":"2.20210520.09.00"}},
        "videoId":"nG_LtCRRZ20"
      },
      {"method":"json"}
    ),
    "url":request-combine(
      "https://www.youtube.com/youtubei/v1/player",
      {"key":"AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"}
    )/url
  })/json/streamingData/(adaptiveFormats)()
  order by boolean($x/width),$x/bitrate
  return
  x"itag={$x/itag} {x:request({"method":"HEAD","error-handling":"xxx=accept","url":$x/url})/headers[1]}"
'
itag=140 HTTP/1.1 200 OK
itag=251 HTTP/1.1 200 OK
itag=278 HTTP/1.1 404 Not Found
itag=160 HTTP/1.1 404 Not Found
itag=242 HTTP/1.1 404 Not Found
itag=133 HTTP/1.1 404 Not Found
itag=243 HTTP/1.1 404 Not Found
itag=134 HTTP/1.1 200 OK
itag=244 HTTP/1.1 404 Not Found
itag=135 HTTP/1.1 404 Not Found
itag=247 HTTP/1.1 404 Not Found
itag=136 HTTP/1.1 404 Not Found
itag=302 HTTP/1.1 404 Not Found
itag=298 HTTP/1.1 200 OK

https://www.benibela.de/documentation/internettools/xpath-functions.html#x-request:

x:request($request as item()*) as map(*)*
Sends an HTTP request. The request parameters are the same as the value accepted by --follow.

It's only because I've asked you about it some years ago that I know how to use x:request(). Because $request as item()* in my opinion is rather vague, I'd say adding some examples would help a lot in explaining how to use the function. And uh, those request parameters accepted by --follow, are the even documented at all? I can't find them. I'd say there's a pretty good chance only you and me know about them.

After having given it another thought, how about this...

x:request($href as xs:string?) as map(*)? Xidel
x:request($href as xs:string?, $options as map(*)) as map(*)? Xidel
Sends an HTTP request and returns the result as a JSON object. The properties "url", "headers" and "raw" correspond to the Xidel global default variables. The property "type" contains the content-type and the property "json", or "doc" contains a JSON, or XML/HTML document.

The properties that may appear in the $options map are as follows:

  • error-handling (string)
  • headers (string, or a sequence of strings)
  • insecure (boolean)
  • method (string)
  • post (string)
  • proxy (string)
  • user-agent (string)

Examples:

x:request("<url>")
x:request("<url>", map{"headers":("<string>","<string>"), "insecure":true(), "user-agent":"<string>"})

I hardly use --follow myself, so I'm not sure what to think of something like -f '<$href as xs:string>' --follow-options 'map{...}'. Perhaps keeps things as they are now (including the "url" / "data" property), but have it accept the same options as $options in x:request().

benibela commented 3 years ago

Sorry for the delay. My system disk died.

I hope you have a backup

Did I? Where? I don't think so.

When you said it is a bug that "Xidel puts out status messages, ignoring -s/--silent."

Haha, are you really asking me? Haven't you tested this yourself at a certain point?

I forgot about it

I'd say, have it accept just one request (as opposed to item()*). With $href and $options it's more in line with doc() and json-doc(). For multiple requests one can easily do for $url in $sequence return x:request($url) in my opinion..

That should not change for backwards compatibility

"insecure" as opposed to "no-check-certificate". As a side note, instead of --no-check-certificate and --ca-certificate, --ca-directory, I'd prefer --insecure, --cacert and --capath, like curl.

I choose the same options as wget

Xidel is more similar to wget than to curl.

Perhaps map{"wait":} should be possible too? Or if xidel -s --wait= -e 'for $url in $sequence return x:request($url)' works as expected (globally for every connection) (haven't tested it), then it shouldn't be necessary, if you ask me.

Should it wait before or after the request?

--error-handling, --header, --no-check-certificate, --method, --post, --proxy and --user-agent should have no effect on x:request(), but --ca-certificate and --ca-directory still should in my opinion. They should work for every connection made.

--method, --post, yes

--error-handling, --no-check-certificate, --proxy, --user-agent might be needed to connect to a certain server. Then it needs to be the same for all requests.

--header could be used to set a username/password authorization for some server...

I made a table for this:

                               Applies to    --follow      x:request        

--data=<string>                                 Xf             -        
--download=<string>                             fX             -       

Extraction options:

  --extract=<string>  or -e                     fX             -                                      ´
  --extract-exclude=<string>                    -              -       
  --extract-include=<string>                    -              -       
  --extract-file=<file>                         fX             -       
  --extract-kind=<string>                       -              -       

  --css=<string>                                fX             -       
  --xpath=<string>                              fX             -       
  --xquery=<string>                             fX             -       
  --xpath2=<string>                             fX             -       
  --xquery1=<string>                            fX             -       
  --xpath3=<string>                             fX             -       
  --xquery3=<string>                            fX             -       
  --xpath3.0=<string>                           fX             -       
  --xquery3.0=<string>                          fX             -       
  --xpath3.1=<string>                           fX             -       
  --xquery3.1=<string>                          fX             -       
  --template-file=<file>                        fX             -       
  --template-action=<string>                    -              -       
  --module=<file>                               Xf             -      
  --module-path=<string>                        Xf             -       

Follow options:

  --follow=<string>  or -f                      n/a               -    
  --follow-kind=<string>                         X                -    
  --follow-exclude=<string>                      X                -    
  --follow-include=<string>                      X                -    
  --follow-file=<file>                           X                -    
  --follow-level=<int>                           X                -  ???
  --allow-repetitions                            X                X    

HTTP connection options:

  --wait=<float>                                 X                -  ???                               
  --user-agent=<string>                          X                X    
  --proxy=<string>                               X                X    
  --post=<string>  or -d                         -                -    
  --form=<string>  or -F                         -                -    
  --method=<string>                              -                -    
  --header=<string>  or -H                       -                -  ????   
  --load-cookies=<string>                        Xf               Xf    
  --save-cookies=<string>                        fX               X  ????
  --print-received-headers                       X                X    
  --error-handling=<string>                      X                X    
  --compressed                                   X                X  ???? 
  --raw-url                                      X                -    

HTTPS connection options:

  --no-check-certificate                         X                X                                   
  --ca-certificate=<file>                        X                X    
  --ca-directory=<string>                        X                X    

Output/Input options:

  --silent or -s                                 X                 X                                  
  --verbose                                      X                 X   
  --default-variable-name=<string>                                n/a  
  --print-variables=<string>                    n/a               n/a  
  --print-type-annotations                      n/a               n/a     
  --hide-variable-names                         n/a               n/a      
  --variable=<string>                            X                n/a     
  --xmlns=<string>                               X                n/a     
  --printed-node-format=<string>                n/a               n/a     
  --printed-json-format=<string>                n/a               n/a     
  --output-format=<string>                      n/a               n/a     

  --output-encoding=<string>                    n/a               n/a     
  --output-declaration=<string>                 n/a               n/a     
  --output-separator=<string>                   n/a               n/a     
  --output-header=<string>                      n/a               n/a     
  --output-footer=<string>                      n/a               n/a     
  --color=<string>                              n/a               n/a      
  --stdin-encoding=<string>                     n/a               n/a    
  --input-format=<string>                       ????              ????
  --xml                                         ????              ????
  --html                                        ????              ????
  --in-place                                                           

Debug options:

  --debug-arguments                               X               n/a                                    
  --trace                                         X               X,n/a     
  --trace-stack                                   X               X,n/a     
  --trace-context                                 X               n/a     

XPath/XQuery compatibility options:

  --json-mode=<string>                            X               X,n/a                    
  --no-json                                       X               X,n/a     
  --no-json-literals                              X                n/a     
  --dot-notation=<string>                         X                n/a
  --no-dot-notation                               X                n/a 
  --only-json-objects                             X                n/a 
  --no-extended-json                              X               X,n/a 
  --strict-type-checking                          X                n/a 
  --strict-namespaces                             X                n/a
  --no-extended-strings                           X                n/a
  --ignore-namespaces                             X                n/a
  --no-optimizations                              X                n/a
  --deprecated-string-options                     X                n/a
  --deprecated-trim-nodes                         X                n/a
  --version                                       n/a              n/a
  --usage                                         n/a              n/a
  --quiet or -q                                   X                n/a

X:    command-line option applies
-:    command-line option does not apply
n/a:  command-line option is unrelated and could not possibly apply
Xf:   command-line option applies if used before the follow
fX:   command-line option does not apply to the follow itself, but uses the data of the follow
X,n/a: Option does not apply to x:request directly, but affects all called function in --extract.
Reino17 commented 3 years ago

I hope you have a backup

I didn't. I mean, who regularly clones his system disk? Not many people, I think.
Windows suddenly froze and my system disk started making ticking sounds. It was already late, so the next day I unscrewed the disk and with the help of a USB docking-station I managed to create a perfect clone of my system disk on another system. I guess I was lucky!
My data disk is still perfectly fine.

When you said it is a bug that "Xidel puts out status messages, ignoring -s/--silent."

In my previous post I proposed command-line options, summed up in the 4th list item, to have no effect on x:request(). Only those. Options like --silent should in my opinion always mute all messages.

That should not change for backwards compatibility

Not so long ago you've pushed some changes that broke backward compatibility, like [ ] --> array{ } for instance. Wouldn't Xidel 0.9.9 be a great milestone to push changes like these?
If not, then no problem. It's your project after all.

Kinda off-topic, but since we're now on the subject of x:request(), something I've already mentioned in a 5 year old e-mail conversation...
It may look like the "headers" property/attribute holds an array, but actually x:request()/type-of(headers) returns sequence*, which is why (in my previous post for instance) you'd have to type headers[1] to grab the first item, instead of the more intuitive (headers)(1)/headers?1 for an array. This is not documented.

--error-handling, --no-check-certificate, --proxy, --user-agent might be needed to connect to a certain server. Then it needs to be the same for all requests.

So something like this...

$ xidel -se '
  x:request({
    "user-agent":"..."
    "url":"..."
  }),
  x:request({
    "no-check-certificate":true(),
    "proxy":"..."
    "url":"..."
  })
'

...will never be possible? Why not? Why do these options need to be the same for all requests?

Btw, if I add --error-handling=200=accept for instance in front of the query in my previous post, then it still sums up the "itag"s and HTTP messages.

I made a table for this:

Very nice!

Should it wait before or after the request?
--wait=<float> X - ???

As in x:request({"wait":<float>}) I'd say before.

--load-cookies=<string> Xf Xf
--save-cookies=<string> fX X ????

Are they really necessary for x:request() (and for --follow too actually) when you can do...

x:request({"headers":file:read-text-lines("cookies.txt")})
x:request(...)/file:write-text-lines("cookies.txt",headers[starts-with(.,"Set-Cookie")])

--compressed X X ????

I forgot to mention this command-line option in my previous post. I would actually like to see --compressed - - in your table, where you can use {"compressed":true()} for --follow and x:request().

--silent or -s X X

This is not the case for r7908, as I demonstrated in my 1st post. But I guess you've already fixed that?

--variable=<string> X n/a

$ xidel -s --variable var -e 'x:request($var)' works just fine. Or do I mis something?

benibela commented 3 years ago

Not so long ago you've pushed some changes that broke backward compatibility, like [ ] --> array{ } for instance.

But there I had no choice. The W3C forced me with their XPath 3.1.

...will never be possible? Why not? Why do these options need to be the same for all requests?

Eventually it will be possible

--load-cookies= Xf Xf --save-cookies= fX X ????

Are they really necessary for x:request() (and for --follow too actually) when you can do...

All cookies are shared between all requests

And once --load-cookies has loaded the cookies, they are there, and it would be difficult to remove them again

I forgot to mention this command-line option in my previous post. I would actually like to see --compressed - - in your table, where you can use {"compressed":true()} for --follow and x:request().

{"compressed":true()} should always be possible

The table is about, if --compressed -e 'request(..)' should be compressed

Also, --compressed and --header do the same. Either they are both used or neither.

This is not the case for r7908, as I demonstrated in my 1st post. But I guess you've already fixed that?

I am working on it

$ xidel -s --variable var -e 'x:request($var)' works just fine. Or do I mis something?

First it reads the variable, and then it calls request on its value, so request does not know there even was a variable.

Reino17 commented 3 years ago

The W3C forced me with their XPath 3.1.

Fair enough.

Any comments btw on x:request()/headers returning a sequence instead of an array?

All cookies are shared between all requests

Only with --load-cookies, right?
I haven't tested it, but I hope...

$ xidel -se '
  x:request({
    "headers":"Cookie: <some-value>"
    "url":"..."
  }),
  x:request({
    "headers":"Cookie: <some-other-value>"
    "url":"..."
  })
'

...works, or will work in the future.

Something else that got me thinking; would x:request({"headers":file:read-text-lines("cookies.txt")}) even work (haven't tested)?

--save-cookies saves cookies as...

Set-Cookie: [...]
Set-Cookie: [...]
[...]

...but as far as I know a request header only accepts...

Cookie: [...]
Cookie: [...]
[...]

So I guess file:read-text-lines("cookies.txt") ! substring(.,5) would then work? I take it --load-cookies does this automatically?
Don't know if x:request({"load-cookies":"<file>"}) even works. Haven't tested this either.

The table is about, if --compressed -e 'request(..)' should be compressed

I know and I'd say no. Same as I said earlier...

Command-line options (like --post, --method and --error-handling) should only affect the direct input (--data=<string>).

...and the same should go for --compressed in my opinion.

Btw, just to be clear. You speak of request(), but the function is in the secondary extension functions category, where the namespace is obligatory. So, you mean x:request(), right?

benibela commented 3 years ago

Any comments btw on x:request()/headers returning a sequence instead of an array?

Sequence is just more natural for XPath.

Only with --load-cookies, right?

Everything stored in the cookie manager, so those loaded with --load-cookies and those send by the servers

Something else that got me thinking; would x:request({"headers":file:read-text-lines("cookies.txt")}) even work (haven't tested)?

You need to write Cookie: .. in the file rather than Set-Cookie: ...

Set-Cookie has more information that Cookie does not have (https, origin domain)

Btw, just to be clear. You speak of request(), but the function is in the secondary extension functions category, where the namespace is obligatory. So, you mean x:request(), right?

yes

Reino17 commented 3 years ago

Sequence is just more natural for XPath.

I have no idea what you mean by that.
If x:request()'s "headers" property holds a sequence, instead of what looks to be an array, then this unusual behavior should be documented!

Everything stored in the cookie manager, so those loaded with --load-cookies and those send by the servers

I'm sorry, but your answer still isn't clear to me. So in my previous post the cookie header in the first x:request() call doesn't affect the second x:request() call?

benibela commented 3 years ago

I have no idea what you mean by that.

Nothing in XPath uses an array, when it can use a sequence

So in my previous post the cookie header in the first x:request() call doesn't affect the second x:request() call?

yes

Reino17 commented 3 years ago

Maybe I'm ignorant, but what does that have to do with anything?

$ xidel -s '{"a":(1,2,3)}' -e '$json'
Error:
err:FOJS0001: Failed to parse JSON: Invalid character at line 1, pos 5: '(' at  (tkColon) in {"a":(1,2,3)}

This obviously returns an error, because sequences aren't allowed in JSON, but creating such a JSON...

$ xidel -se '{"a":(1,2,3)},{"a":array{1,2,3}}' --printed-json-format=compact
{"a": [1, 2, 3]}
{"a": [1, 2, 3]}

$ xidel -se '{"a":(1,2,3)}/type-of(a),{"a":array{1,2,3}}/type-of(a)'
sequence*
array()

...surprisingly is allowed. But I assume this is not you, but something the "Joint XML Query Working Group and XSL Working Group" came up with? If I were to have a say in this, I'd say, don't allow creating sequences in JSON like this, because it will only cause confusion.

If I really am ignorant, please enlighten me.

benibela commented 3 years ago

I came up with it, so Xidel can be used to edit JSON more easily.

The "Joint XML Query Working Group and XSL Working Group" came up with this output:


$ xidel -se '( {"a":(1,2,3)},{"a":array{1,2,3}} ) ! serialize(., map{"method": "json"})' 
Error:
err:SERE0023: Serialization error, when serializing (1, 2, 3)
Possible backtrace:
  $0000000000505A7A  ERROR,  line 3609 of ../../../components/pascal/data/xquery.pas: perhaps Q{http://www.benibela.de/2012/pxp/extensions}match + 75098 ? but unlikely
  $0000000000536CB5  EVALUATE,  line 3220 of ../../../components/pascal/data/xquery_terms.inc: perhaps TXQTermBinaryOp + 3157 ? but unlikely

Call xidel with --trace-stack to get an actual backtrace
$ xidel -se '( {"a":(1,2,3)},{"a":array{1,2,3}} ) ! serialize(., map{"method": "adaptive"})' 
map{"a":(1,2,3)}
map{"a":[1,2,3]}
benibela commented 3 years ago

Also, for more confusion there is the JSONiq mode:


$ xidel --json-mode jsoniq -se '( {"a":(1,2,3)},{"a":array{1,2,3}} ) ! serialize(., map{"method": "json"})' 
{"a":[1,2,3]}
{"a":[1,2,3]}
$ xidel --json-mode jsoniq -se '( {"a":(1,2,3)},{"a":array{1,2,3}} ) ! serialize(., map{"method": "adaptive"})' 
map{"a":[1,2,3]}
map{"a":[1,2,3]}