Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse single value as array #434

Closed
JoseVSeb opened this issue Mar 9, 2022 · 15 comments · Fixed by #441
Closed

parse single value as array #434

JoseVSeb opened this issue Mar 9, 2022 · 15 comments · Fixed by #441

Comments

@JoseVSeb
Copy link

JoseVSeb commented Mar 9, 2022

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.

@ljharb
Copy link
Owner

ljharb commented Mar 9, 2022

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.

@jerrylian-db
Copy link

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?

@jerrylian-db
Copy link

jerrylian-db commented Jun 4, 2022

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.

@ljharb
Copy link
Owner

ljharb commented Jun 4, 2022

@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)

@jerrylian-db
Copy link

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'.

@ljharb
Copy link
Owner

ljharb commented Jun 5, 2022

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

@jerrylian-db
Copy link

@ljharb It does! Thanks so much!

@ljharb ljharb closed this as completed in 4e44019 Jun 6, 2022
@jerrylian-db
Copy link

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

@ljharb
Copy link
Owner

ljharb commented Jun 6, 2022

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

@ljharb
Copy link
Owner

ljharb commented Jun 6, 2022

Went ahead and published v6.10.4.

@jerrylian-db
Copy link

@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?

@ljharb
Copy link
Owner

ljharb commented Jun 6, 2022

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

I'll get a fix out.

ljharb added a commit that referenced this issue Jun 6, 2022
…licit `[]` on a single-item array

Properly fixes #434
@ljharb
Copy link
Owner

ljharb commented Jun 6, 2022

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

@ljharb
Copy link
Owner

ljharb commented Jun 27, 2022

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

@robations

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants