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

If { allowDots: true } is passed, dots in property names should be encoded #249

Closed
abirmingham opened this issue Feb 2, 2018 · 3 comments · Fixed by #488
Closed

If { allowDots: true } is passed, dots in property names should be encoded #249

abirmingham opened this issue Feb 2, 2018 · 3 comments · Fixed by #488

Comments

@abirmingham
Copy link

abirmingham commented Feb 2, 2018

Currently Qs.stringify({ 'this.is.unfortunate': 2}, { allowDots: true }) results in 'this.is.unfortunate=2', which is ambiguous. 'this.is.unfortunate=2' can be interpreted as both { this: { is: { unfortunate: 2 } }, and also { 'this.is': { unfortunate: 2 } }, and also { this: { 'is.unfortunate': 2 } }. Truly this is unfortunate - see my fiddle here.

I propose that Qs.stringify() encode keys that dots are escaped and Qs.parse() decode keys in the reverse manner. The goal would be to make Qs.parse(Qs.stringify(x)) lossless - at least in this particular case.

Open Questions:

  1. Is this a good idea?
  2. What encoding should be used?
  3. Should encoding always occur, or only when { allowDots: true } is passed?

Thank you!

@ljharb
Copy link
Owner

ljharb commented Mar 1, 2018

I'm not sure I understand; there's multiple parsing modes in qs; when you choose one, you're responsible for knowing (somehow) that you need to choose the same one on the other end. Specifically, parse(stringify(x, opts), opts) should always be the same for any opts.

Can you elaborate on your proposal?

@abirmingham
Copy link
Author

abirmingham commented Mar 2, 2018

Sure.

The issue is not that the serialization options need to be tracked from the time of serialization to the time of deserialization. This would be nice in theory, but I'm not sure if it's possible in practice. At any rate, this issue is not that.

This issue is about lossiness when serializing objects with the allow-dot option, in the case where those objects have string keys which themselves contain dots.

Imagine you have an object that needs to be serialized for the url, but the object looks like this: { "name.obj": { "first": "John", "last": "Doe" } }.

Let us first assume that we must use allowDots for cleanliness of our URLs and consistency across our application.

We cannot serialize this object using the qs library. Or, rather, we can serialize it but in doing so we create a string that cannot be deserialized to the original content. In other words, the serialization is lossy.

This proposal is to encode the dot so that the case is lossless.

@ljharb
Copy link
Owner

ljharb commented Mar 2, 2018

aha, I see what you mean:

const opts = { allowDots: true };
qs.parse(qs.stringify({ "name.obj": { "first": "John", "last": "Doe" } }, opts), opts)
 // => { name: { obj: { first: 'John', last: 'Doe' } } }

And you'd propose that qs.stringify({ "name.obj": { "first": "John", "last": "Doe" } }, opts) produce, instead of name.obj.first=John&name.obj.last=Doe, name%2Eobj.first=John&name%2Eobj.last=Doe, such that qs.parse('name%2Eobj.first=John&name%2Eobj.last=Doe') would produce something like the original object? (currently, it doesn't do that; the %2E is decoded and the same behavior occurs).

Can you think of a way to do this that would be semver-minor? Perhaps we could add a new option to both parse and stringify, that could not be supplied along with allowDots but would imply it, and handled this case - but I'm not sure how that implementation would look.

Would you be interested in trying a PR?

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