Skip to content

Commit 5b63418

Browse files
committed
uri encode brackets for #8, dont omit equals signs when value is null for #9, update readme
1 parent fd10bca commit 5b63418

File tree

3 files changed

+63
-23
lines changed

3 files changed

+63
-23
lines changed

README.md

+43-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var obj = Qs.parse('a=c'); // { a: 'c' }
1717
var str = Qs.stringify(obj); // 'a=c'
1818
```
1919

20-
### Objects
20+
### Parsing Objects
2121

2222
**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
2323
For example, the string `'foo[bar]=baz'` converts to:
@@ -30,6 +30,13 @@ For example, the string `'foo[bar]=baz'` converts to:
3030
}
3131
```
3232

33+
URI encoded strings work too:
34+
35+
```javascript
36+
Qs.parse('a%5Bb%5D=c');
37+
// { a: { b: 'c' } }
38+
```
39+
3340
You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
3441

3542
```javascript
@@ -72,7 +79,7 @@ Qs.parse('a[b][c][d][e][f][g][h][i]=j', 1);
7279

7380
The depth limit mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.
7481

75-
### Arrays
82+
### Parsing Arrays
7683

7784
**qs** can also parse arrays using a similar `[]` notation:
7885

@@ -127,3 +134,37 @@ You can also create arrays of objects:
127134
Qs.parse('a[][b]=c');
128135
// { a: [{ b: 'c' }] }
129136
```
137+
138+
### Stringifying
139+
140+
When stringifying, **qs** always URI encodes output. Objects are stringified as you would expect:
141+
142+
```javascript
143+
Qs.stringify({ a: 'b' });
144+
// 'a=b'
145+
Qs.stringify({ a: { b: 'c' } });
146+
// 'a%5Bb%5D=c'
147+
```
148+
149+
Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
150+
151+
When arrays are stringified, they are always given explicit indices:
152+
153+
```javascript
154+
Qs.stringify({ a: ['b', 'c', 'd'] });
155+
// 'a[0]=b&a[1]=c&a[2]=d'
156+
```
157+
158+
Empty strings and null values will omit the value, but the equals sign (=) remains in place:
159+
160+
```javascript
161+
Qs.stringify({ a: '' });
162+
// 'a='
163+
```
164+
165+
Properties that are set to `undefined` will be omitted entirely:
166+
167+
```javascript
168+
Qs.stringify({ a: null, b: undefined });
169+
// 'a='
170+
```

lib/stringify.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,22 @@ internals.stringify = function (obj, prefix) {
1414
else if (obj instanceof Date) {
1515
obj = obj.toISOString();
1616
}
17+
else if (obj === null) {
18+
obj = '';
19+
}
1720

1821
if (typeof obj === 'string' ||
1922
typeof obj === 'number' ||
2023
typeof obj === 'boolean') {
2124

22-
return [prefix + '=' + encodeURIComponent(obj)];
23-
}
24-
25-
if (obj === null) {
26-
return [prefix];
25+
return [encodeURIComponent(prefix) + '=' + encodeURIComponent(obj)];
2726
}
2827

2928
var values = [];
3029

3130
for (var key in obj) {
3231
if (obj.hasOwnProperty(key)) {
33-
values = values.concat(internals.stringify(obj[key], prefix + '[' + encodeURIComponent(key) + ']'));
32+
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']'));
3433
}
3534
}
3635

@@ -44,7 +43,7 @@ module.exports = function (obj) {
4443

4544
for (var key in obj) {
4645
if (obj.hasOwnProperty(key)) {
47-
keys = keys.concat(internals.stringify(obj[key], encodeURIComponent(key)));
46+
keys = keys.concat(internals.stringify(obj[key], key));
4847
}
4948
}
5049

test/stringify.js

+14-14
Original file line numberDiff line numberDiff line change
@@ -30,49 +30,49 @@ describe('#stringify', function () {
3030

3131
it('stringifies a nested object', function (done) {
3232

33-
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a[b]=c');
34-
expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a[b][c][d]=e');
33+
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
34+
expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e');
3535
done();
3636
});
3737

3838
it('stringifies an array value', function (done) {
3939

40-
expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a[0]=b&a[1]=c&a[2]=d');
40+
expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d');
4141
done();
4242
});
4343

4444
it('stringifies a nested array value', function (done) {
4545

46-
expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a[b][0]=c&a[b][1]=d');
46+
expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
4747
done();
4848
});
4949

5050
it('stringifies an object inside an array', function (done) {
5151

52-
expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a[0][b]=c');
53-
expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a[0][b][c][0]=1');
52+
expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c');
53+
expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1');
5454
done();
5555
});
5656

5757
it('stringifies a complicated object', function (done) {
5858

59-
expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a[b]=c&a[d]=e');
59+
expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e');
6060
done();
6161
});
6262

6363
it('stringifies an empty value', function (done) {
6464

6565
expect(Qs.stringify({ a: '' })).to.equal('a=');
6666
expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b=');
67-
expect(Qs.stringify({ a: null })).to.equal('a');
68-
expect(Qs.stringify({ a: { b: null } })).to.equal('a[b]');
67+
expect(Qs.stringify({ a: null })).to.equal('a=');
68+
expect(Qs.stringify({ a: { b: null } })).to.equal('a%5Bb%5D=');
6969
done();
7070
});
7171

7272
it('drops keys with a value of undefined', function (done) {
7373

7474
expect(Qs.stringify({ a: undefined })).to.equal('');
75-
expect(Qs.stringify({ a: { b: undefined, c: null } })).to.equal('a[c]');
75+
expect(Qs.stringify({ a: { b: undefined, c: null } })).to.equal('a%5Bc%5D=');
7676
done();
7777
});
7878

@@ -100,24 +100,24 @@ describe('#stringify', function () {
100100

101101
Object.prototype.crash = 'test';
102102
expect(Qs.stringify({ a: 'b'})).to.equal('a=b');
103-
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a[b]=c');
103+
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
104104
delete Object.prototype.crash;
105105
done();
106106
});
107107

108108
it('stringifies boolean values', function (done) {
109109

110110
expect(Qs.stringify({ a: true })).to.equal('a=true');
111-
expect(Qs.stringify({ a: { b: true } })).to.equal('a[b]=true');
111+
expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true');
112112
expect(Qs.stringify({ b: false })).to.equal('b=false');
113-
expect(Qs.stringify({ b: { c: false } })).to.equal('b[c]=false');
113+
expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false');
114114
done();
115115
});
116116

117117
it('stringifies buffer values', function (done) {
118118

119119
expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test');
120-
expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a[b]=test');
120+
expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test');
121121
done();
122122
});
123123
});

0 commit comments

Comments
 (0)