qs: Option to not collapse arrays with missing indices.

The following test:

$ node -pe 'require("qs").parse("option[1]=1&text[0]=&text[1]=&text[2]=")'

Produces:

{ option: [ '1' ], text: [ '', '', '' ] }

The option array is collapsed and the missing index 0 is removed. According to the qs documentation, this behavior is intentional. However, it causes problems when handling arrays of checkboxes on a form with e.g. body-parser.

Consider:

index.js:

var express = require("express");
var site = express();

site.use(require("body-parser").urlencoded({extended:true}));

site.get("/", function (req, res) {
	res.sendFile(__dirname + "/test.html");
});

site.post("/form", function (req, res) {
	res.json(req.body);
});

site.listen(8081);

test.html:

<html>
	<body>
		<form method="post" action="/form">
			<input type="checkbox" name="option[0]" value="1">
			<input type="checkbox" name="option[1]" value="1">
			<input type="checkbox" name="option[2]" value="1"><br>
			<input type="text" name="text[0]"><br>
			<input type="text" name="text[1]"><br>
			<input type="text" name="text[2]"><br>
			<button type="submit">Submit</button>
		</form>
	</body>
</html>

In that example, if I were to check only option[1], I verify that the index is set correctly by inspecting the request in Chrome, body contents are:

option[1]:1
text[0]:
text[1]:
text[2]:

Yet qs collapses the option array and body-parser ultimately produces the following in req.body:

{"option":["1"],"text":["","",""]}

Similarly, if I were to check option[0] and option[2], the request body would have:

option[0]:1
option[2]:1
text[0]:
text[1]:
text[2]:

But it would be parsed to:

{"option":["1","1"],"text":["","",""]}

All information about which checkbox was checked is lost.

The only way, then, to parse checkbox arrays on forms is workarounds like the clever but hacky one at http://stackoverflow.com/a/41093135, which basically involves hidden inputs with the same name as the checkboxes, then using Array.isArray() to determine if a result is true or false.

I had originally opened this issue at https://github.com/expressjs/body-parser/issues/211 before I knew it was qs.

To that end, I’d like an option to not collapse arrays, such that the previous examples yield things like:

{"option":[null,"1"]}

Or:

{"option":["1",null,"1"]}

This would make parsing arrays of checkboxes on forms possible, which it currently is not without trickery.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 1
  • Comments: 16 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I’m running into this limitation as well. My situation looks like this, with radio buttons:

Title       1     2     3  
--------------------------
Foo        ( )   ( )   ( )
Bar        (*)   ( )   ( )
Baz        ( )   (*)   ( )
Qux        ( )   ( )   (*)

So the user chooses a value between 1-3, or nothing at all, for each title. The titles are dynamically generated. In this case, you need the “indexed field names”, like title[0], title[1], etc. to determine which title each value applies to. I can work around this using the trick with hidden fields, but it would be nice if this was supported out of the box!

You can dynamically add option[] with no numerical indices - there’s zero reason you need them for single options - if you have multiple options, the better choice would be to pick a unique alphanumeric name - for example, description["item_" + n] is a very straightforward choice, is trivial to iterate on the other side.

I’ll think more about this option - creating sparse arrays is just not a good idea, so it’d need to respect arrayLimit and also pick null or undefined to fill out the empty items.