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

Parsing bug #188

Closed
BenjaminHorn opened this issue Dec 20, 2016 · 2 comments
Closed

Parsing bug #188

BenjaminHorn opened this issue Dec 20, 2016 · 2 comments

Comments

@BenjaminHorn
Copy link

this parameter string
[where][and][0][valid]=true&filter[where][and][1][or][0][name]=test&filter[where][and][1][or][1][deleted]=false

gets parsed as:

{"and":[{"valid":true},{"or":[{"[name]":"test"},{"[deleted]":false}]}]}
note the square brackets around name and deleted

We found this behavior in Loopback, which uses this module for parsing parameters. @raymondfeng suggested to open here an issue, and kindly provided a snippet to demonstrate the problem:

var assert = require('assert');
var qs = require('qs');
var json = {filter: {where: {"and":[{"valid":true},{"or":[{"name":"test"},{"deleted":false}]}]}}};
var x = qs.stringify(json, { encode: false });
var y = qs.parse(x);
console.log('%j %j', json, y);
assert.deepEqual(json, y); // will throw

Here you can find the original issue,

@ljharb
Copy link
Owner

ljharb commented Dec 20, 2016

The differences I see in your example are:

  • json.filter.where.and[0].valid === true but y.filter.where.and[0].value === 'true'
  • $.filter.where.and[1].or:
    • json.filter.where.and[1].or => [ { name: 'test' }, { deleted: false } ]
    • y.filter.where.and[1].or => [ { '[name]': 'test' }, { '[deleted]': 'false' } ]

qs.parse has a default depth option of 5 (documented in the readme) - if you do var y = qs.parse(x, { depth: 10 });, for example, you'll get the following:

  • $.filter.where.and[1].or:
    • json.filter.where.and[1].or => [ { name: 'test' }, { deleted: false } ]
    • y.filter.where.and[1].or => [ { 'name': 'test' }, { 'deleted': 'false' } ]

which only leaves the booleans on the front end of the pipeline ending up as strings on the back end.

Parsing booleans is #91 (comment) - since everything that comes in via a querystring is a string, there's no safe way to automatically parse the strings "true" and "false" into booleans without breaking places with the actual string true or false. However, you can pass an explicit decoder option to qs.parse that handles this.

Note the following, with changes:

var assert = require('assert');
var qs = require('qs');
var json = {filter: {where: {"and":[{"valid":true},{"or":[{"name":"test"},{"deleted":false}]}]}}};
var x = qs.stringify(json, { encode: false });
var y = qs.parse(x, { depth: 10, decoder: function (x) { if (x === 'true') { return true; } else if (x === 'false') { return false; } return x; } });
console.log('%j %j', json, y);
assert.deepEqual(json, y); // will NOT throw

@jackypan1989

This comment has been minimized.

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

No branches or pull requests

3 participants