├── .gitignore ├── README.md ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # form-data-to-object 2 | Converts application/x-www-form-urlencoded data structure to plain JS object 3 | 4 | ## API 5 | - `formData.toObj()` - Converts keys in x-www-form-urlencoded format on an object to a normal object 6 | - `formData.fromObj()` - Converts a normal object to object with keys in x-www-form-urlencoded format 7 | ```js 8 | 9 | // keys in x-www-form-urlencoded format 10 | { 11 | 'foo': 'bar', 12 | 'foo2[name]': 'bar2', 13 | 'foo3[0]': 'bar3', 14 | 'foo3[1][name]': 'bar4' 15 | } 16 | 17 | // converts from/to 18 | 19 | // normal object 20 | { 21 | foo: 'bar', 22 | foo2: { 23 | name: 'bar2' 24 | }, 25 | foo3: ['bar3', { 26 | name: 'bar4' 27 | }] 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function toObj(source) { 2 | return Object.keys(source).reduce(function (output, key) { 3 | var parentKey = key.match(/[^\[]*/i); 4 | var paths = key.match(/\[.*?\]/g) || []; 5 | paths = [parentKey[0]].concat(paths).map(function (key) { 6 | return key.replace(/\[|\]/g, ''); 7 | }); 8 | var currentPath = output; 9 | while (paths.length) { 10 | var pathKey = paths.shift(); 11 | 12 | if (pathKey in currentPath) { 13 | currentPath = currentPath[pathKey]; 14 | } else { 15 | currentPath[pathKey] = paths.length ? isNaN(paths[0]) ? {} : [] : source[key]; 16 | currentPath = currentPath[pathKey]; 17 | } 18 | } 19 | 20 | return output; 21 | }, {}); 22 | } 23 | 24 | function fromObj(obj) { 25 | function recur(newObj, propName, currVal) { 26 | if (Array.isArray(currVal) || Object.prototype.toString.call(currVal) === '[object Object]') { 27 | Object.keys(currVal).forEach(function(v) { 28 | recur(newObj, propName + "[" + v + "]", currVal[v]); 29 | }); 30 | return newObj; 31 | } 32 | 33 | newObj[propName] = currVal; 34 | return newObj; 35 | } 36 | 37 | var keys = Object.keys(obj); 38 | return keys.reduce(function(newObj, propName) { 39 | return recur(newObj, propName, obj[propName]); 40 | }, {}); 41 | } 42 | 43 | module.exports = { 44 | fromObj: fromObj, 45 | toObj: toObj 46 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "form-data-to-object", 3 | "version": "0.2.0", 4 | "description": "Converts application/x-www-form-urlencoded keys to plain JS object", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "nodeunit test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/christianalfoni/form-data-to-object.git" 12 | }, 13 | "keywords": [ 14 | "object", 15 | "form", 16 | "x-www-form-urlencoded" 17 | ], 18 | "author": "Christian Alfoni", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/christianalfoni/form-data-to-object/issues" 22 | }, 23 | "homepage": "https://github.com/christianalfoni/form-data-to-object", 24 | "devDependencies": { 25 | "nodeunit": "^0.9.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var lib = require('./index.js'); 2 | var fromObj = lib.fromObj; 3 | var toObj = lib.toObj; 4 | 5 | exports['toObj: should not convert plain text'] = function(test) { 6 | var input = { 7 | 'foo': 'bar' 8 | }; 9 | test.deepEqual(toObj(input), { 10 | 'foo': 'bar' 11 | }); 12 | test.done(); 13 | }; 14 | 15 | exports['toObj: should convert brackets with text to nested objects'] = function(test) { 16 | var input = { 17 | 'foo[bar]': 'bar' 18 | }; 19 | test.deepEqual(toObj(input), { 20 | 'foo': { 21 | 'bar': 'bar' 22 | } 23 | }); 24 | test.done(); 25 | }; 26 | 27 | exports['toObj: should convert brackets with numbers to nested arrays'] = function(test) { 28 | var input = { 29 | 'foo[0]': 'bar' 30 | }; 31 | test.deepEqual(toObj(input), { 32 | 'foo': ['bar'] 33 | }); 34 | test.done(); 35 | }; 36 | 37 | exports['toObj: should convert complex structures to object and arrays respectively'] = function(test) { 38 | var input = { 39 | 'foo[0]': 'bar', 40 | 'foo[1]': 'bar2', 41 | 'foo[2][foo]': 'bar', 42 | 'foo[3][0][foo]': 'bar', 43 | 44 | }; 45 | test.deepEqual(toObj(input), { 46 | 'foo': ['bar', 'bar2', {foo: 'bar'}, [{foo: 'bar'}]] 47 | }); 48 | test.done(); 49 | }; 50 | 51 | exports['fromObj: transform of transform should be equal to original (aka fromObj(toObj(obj)) === obj)'] = function(test) { 52 | var input = { 53 | 'foo[0]': 'bar', 54 | 'foo[1]': 'bar2', 55 | 'foo[2][foo]': 'bar', 56 | 'foo[3][0][foo]': 'bar', 57 | 58 | }; 59 | test.deepEqual(fromObj(toObj(input)), input); 60 | test.done(); 61 | }; 62 | 63 | exports['fromObj: should convert homogeneous arrays'] = function(test) { 64 | var input = { 65 | 'foo': ['bar', 'bar2'], 66 | }; 67 | test.deepEqual(fromObj(input), { 68 | 'foo[0]': 'bar', 69 | 'foo[1]': 'bar2', 70 | }); 71 | test.done(); 72 | }; 73 | 74 | exports['fromObj: should convert heterogeneous arrays'] = function(test) { 75 | var input = { 76 | 'foo': ['bar', 1, ['bar2', 'bar3']], 77 | }; 78 | test.deepEqual(fromObj(input), { 79 | 'foo[0]': 'bar', 80 | 'foo[1]': 1, 81 | 'foo[2][0]': 'bar2', 82 | 'foo[2][1]': 'bar3', 83 | }); 84 | test.done(); 85 | }; 86 | 87 | exports['fromObj: should convert nested objects'] = function(test) { 88 | var input = { 89 | 'foo': { 90 | 'foo2': 'bar1', 91 | 'foo3': { 92 | 'foo4': 'bar2', 93 | 'foo5': ['bar3'], 94 | } 95 | }, 96 | }; 97 | test.deepEqual(fromObj(input), { 98 | 'foo[foo2]': 'bar1', 99 | 'foo[foo3][foo4]': 'bar2', 100 | 'foo[foo3][foo5][0]': 'bar3', 101 | }); 102 | test.done(); 103 | }; 104 | --------------------------------------------------------------------------------