Skip to content

Commit

Permalink
allow users to supply custom parsing function to parse incoming values
Browse files Browse the repository at this point in the history
  • Loading branch information
paul42 committed Oct 26, 2017
1 parent a80f52b commit 9099b00
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 46 deletions.
40 changes: 22 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,33 +146,37 @@ var withDots = qs.parse('a.b=c', { allowDots: true });
assert.deepEqual(withDots, { a: { b: 'c' } });
```

### Parsing Arrays
### Overriding how values are parsed

**qs** can also parse arrays using a similar `[]` notation:
**qs** can take an optional `valueParser` function as an option - this will parse the value before it is assigned

```javascript
var withArray = qs.parse('a[]=b&a[]=c');
assert.deepEqual(withArray, { a: ['b', 'c'] });
```
**qs** can split strings into an array:
signature: Function that takes key and val, and returns the desired parsed value. in this example, Key would be `a[]` and value would be `b,c`. the return value is an array of `['b', 'c']`

```javascript
var multiItemArray = qs.parse('a[]=b,c');
assert.deepEqual(multiItemArray, { a: ['b', 'c'] });
```

you can change the array delimiter:
var parseCommaDelimitedString = function (key, val) {
var brackets = /(\[[^[\]]*])/;
var returnVal = val;

```javascript
var multiItemArray = qs.parse('a[]=b;c', { arrayDelimiter: ';'});
assert.deepEqual(multiItemArray, { a: ['b', 'c'] });
if (val !== null
&& brackets.test(key)
&& typeof val === 'string'
&& val.indexOf(',') !== -1)
{
returnVal = val.split(',');
}
return returnVal;
};
var customParsedObject = qs.parse('a[]=b,c', { valueParser: parseCommaDelimitedString })
assert.deepEqual(customParsedObject, { a: ['b', 'c'] })
```

you can disable splitting arrays:
### Parsing Arrays

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

```javascript
var singleString = qs.parse('a[]=b,c', { splitArrays: false});
assert.deepEqual(singleString, { a: ['b,c'] });
var withArray = qs.parse('a[]=b&a[]=c');
assert.deepEqual(withArray, { a: ['b', 'c'] });
```

You may specify an index as well:
Expand Down
33 changes: 10 additions & 23 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ var has = Object.prototype.hasOwnProperty;
var defaults = {
allowDots: false,
allowPrototypes: false,
arrayDelimiter: ',',
arrayLimit: 20,
decoder: utils.decode,
delimiter: '&',
Expand All @@ -17,23 +16,6 @@ var defaults = {
strictNullHandling: false
};

var splitString = function (key, val, options) {
var brackets = /(\[[^[\]]*])/;
var returnVal = val;

if (
val !== null
&& options.splitArrays
&& brackets.test(key)
&& typeof val === 'string'
&& val.indexOf(options.arrayDelimiter) !== -1
) {
returnVal = val.split(options.arrayDelimiter);
}

return returnVal;
};

var parseValues = function parseQueryStringValues(str, options) {
var obj = {};
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
Expand All @@ -54,11 +36,14 @@ var parseValues = function parseQueryStringValues(str, options) {
key = options.decoder(part.slice(0, pos), defaults.decoder);
val = options.decoder(part.slice(pos + 1), defaults.decoder);
}

if (options.valueParser !== null) {
val = options.valueParser(key, val, options);
}

if (has.call(obj, key)) {
val = splitString(key, val, options);
obj[key] = [].concat(obj[key]).concat(val);
} else {
val = splitString(key, val, options);
obj[key] = val;
}

Expand Down Expand Up @@ -163,15 +148,17 @@ module.exports = function (str, opts) {
throw new TypeError('Decoder has to be a function.');
}

if (options.valueParser !== null && options.valueParser !== undefined && typeof options.valueParser !== 'function') {
throw new TypeError('valueParser has to be a function.');
}

options.ignoreQueryPrefix = options.ignoreQueryPrefix === true;
options.delimiter = typeof options.delimiter === 'string' || utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
options.arrayDelimiter = typeof options.arrayDelimiter === 'string' || utils.isRegExp(options.arrayDelimiter)
? options.arrayDelimiter : defaults.arrayDelimiter;
options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
options.parseArrays = options.parseArrays !== false;
options.splitArrays = options.splitArrays !== false;
options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
options.valueParser = typeof options.valueParser === 'function' ? options.valueParser : null;
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots;
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
Expand Down
24 changes: 19 additions & 5 deletions test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,33 @@ test('parse()', function (t) {

t.test('parses an explicit array', function (st) {
st.deepEqual(qs.parse('a[]=b'), { a: ['b'] });
st.deepEqual(qs.parse('a[]=b,c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a[]=c'), { a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a[]=c&a[]=d'), { a: ['b', 'c', 'd'] });
st.end();
});

t.test('parses an explicit array with different array delimiter', function (st) {
st.deepEqual(qs.parse('a[]=b;c', { arrayDelimiter: ';' }), { a: ['b', 'c'] });
t.test('parses a value with the supplied function', function (st) {
st.deepEqual(qs.parse('a[]=b,c', { valueParser: function (key, val) {
var brackets = /(\[[^[\]]*])/;
var returnVal = val;

if (
val !== null
&& brackets.test(key)
&& typeof val === 'string'
&& val.indexOf(',') !== -1
) {
returnVal = val.split(',');
}
return returnVal;
} }), { a: ['b', 'c'] });
st.end();
});

t.test('parses an explicit array but does not split on delimiter if desired', function (st) {
st.deepEqual(qs.parse('a[]=b,c', { splitArrays: false }), { a: ['b,c'] });
t.test('throws error with badly crafted valueParser', function (st) {
st['throws'](function () {
qs.parse({}, { valueParser: 'notAFunction' });
}, new TypeError('valueParser has to be a function.'));
st.end();
});

Expand Down

0 comments on commit 9099b00

Please sign in to comment.