ljharb / qs

A querystring parser with nesting support

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

parse single value as array

JoseVSeb opened this issue · comments

I was looking for a good query parser including URLSearchParams, query-string and qs.
But each is lacking a feature that at least one other has.

Say, I have a query string "a=1" and another "a=1,2".
Right now, if I use a comma-separated query parser, then the output would be {a:1} and {a:[1,2]} respectively.
But I want my app to get an array regardless; like so {a:[1]} and {a:[1,2]} respectively. Sure, I could handle that in my app itself, but in real-world scenarios, I would likely have many query params; manually handling each one is not ideal, especially for something like this that should be handled by the parser.

In URLSearchParams, it's possible via getAll function instead of get, but it lacks so many other essential features like having support for comma separated parsing.

It would be great to have an option to force parse certain keys as arrays and not arrays, regardless of the default inferred format; it would be more code friendly rather than having to write exceptions for every little scenario.

The proper way to ensure that is to have your query strings be a[]=1 and a[]=1&a[]=2, with arrayFormat brackets.

Any time you want to get an array regardless, you can do [].concat(item) (or [].concat(item || []), etc) when fetching from the object, but I think the better approach is to use the explicit bracketed form.

I could envision a parse option that takes an array of key names, and always makes those be arrays, but I'm not yet convinced that's a valuable thing to allow.

Hi @ljharb, bumping this thread. Like @JoseVSeb, I would like to have comma separated query string for stringifying arrays, because in my use-case, I have an array of many many items and I don't want the URL to be too long.

However, for an object like {a: [1]}, when I stringify it with arrayFormat: "comma" and then parse it with comma: true, it becomes {a: 1}. How can I customize the encoding logic to use bracket format for one item, but comma format for more than one item? I imagine that would fix the issue.

Would it be possible to encode using a format like "a[]=1,2" so that both single and multiple item scenarios are interpreted correctly?

Another option I'm thinking about is that when arrayFormat: "comma" is turned on, if the array length is 1, let it be encoded with the bracket format a[]=1.

Edit: some changes to these lines should suffice.

@jerrylian-db a[]=1,2 is the string "1,2" as an array item.

Unfortunately the comma arrayFormat is a bit weird. qs.stringify({a: [1, 2]}, { arrayFormat: 'comma', encode: false }) produces 'a=1,2', and then qs.parse('a=1,2', { comma: true }) produces { a: [ '1', '2' ] } - which is correct.

However, qs.parse('a=1', { comma: true }) produces { a: '1' }, which isn't what you want. qs.parse('a=1,', { comma: true }) produces { a: [ '1', '' ] }, so that doesn't work either.

qs.parse('a[]=1', { comma: true }) does produce { a: [ '1' ] } but qs.parse('a[]=1,2', { comma: true }) produces { a: [ [ '1', '2' ] ] }.

So, it seems like perhaps stringify, with arrayFormat comma, should be turning a single-item array into a bracket-suffixed key. Would that work for your purposes? (also @JoseVSeb)

Yes, I think that would! Here's the behavior I'm looking for:

  • qs.stringify({a: [1, 2]}, { arrayFormat: 'comma', encode: false }) === 'a=1,2'
  • qs.stringify({a:[1]}, { arrayFormat: 'comma', encode: false }) ==qs= 'a[]=1'

That way, arrays of all (non-zero) sizes can be properly encoded and decoded when arrayFormat='comma'.

@jerrylian-db can you confirm #441 works for you?

@ljharb It does! Thanks so much!

@ljharb Quick question: when will these changes be released?

There's no release schedule or anything, but I'll likely have this out soon.

Went ahead and published v6.10.4.

@ljharb It seems that your PR has led to some errors. Now when I run qs.stringify({ a: 'c' }, { encodeValuesOnly: true, arrayFormat: 'comma' } I get a[] = c rather than a=c. Also, is there a reason you decided to only add this fix to when envodeValuesOnly is set to true?

hmm, that does seem wrong - but the presence of encodeValuesOnly shouldn't be relevant.

I'll get a fix out.

0e903c0 should cover this, it'll be out in v6.10.5 shortly.

This ended up being reverted by default, but v6.11.0 adds a commaRoundTrip option to get the behavior you want.

const query = "a=1&a=2,3";
Array.from(new URLSearchParams(query))
    .map((k, v] => [k, v.split(",")]);

// [
//   [ 'a', [ '1 ] ],
//   [ 'a', [ '2', '3' ] ],
// ]

This did the job for me, if anyone's looking for predictable arrays with comma parsing. YMMV.

In my project the a[] style added extra bytes we don't need.