我如何将条目从HTML5 FormData对象转换为JSON?
解决方案不应该使用jQuery。而且,它不应该简单地序列化整个FormData对象,而应该只序列化它的键/值条目。
我如何将条目从HTML5 FormData对象转换为JSON?
解决方案不应该使用jQuery。而且,它不应该简单地序列化整个FormData对象,而应该只序列化它的键/值条目。
当前回答
使用JSON.stringify()中描述的toJSON
如果值具有toJSON()方法,则它负责定义将序列化的数据。
这里有一个小技巧。
var fd = new FormData(document.forms[0]); fd.toJSON = function() { const o = {}; this.forEach((v, k) => { v = this.getAll(k); o[k] = v.length == 1 ? v[0] : (v.length >= 1 ? v : null); }); return o; }; document.write(`<pre>${JSON.stringify(fd)}</pre>`) <form action="#"> <input name="name" value="Robinson" /> <input name="items" value="Vin" /> <input name="items" value="Fromage" /> <select name="animals" multiple id="animals"> <option value="tiger" selected>Tigre</option> <option value="turtle" selected>Tortue</option> <option value="monkey">Singe</option> </select> </form>
其他回答
这里有一种更函数化的方式,不需要使用库。
Array.from(formData.entries()).reduce((memo, [key, value]) => ({
...memo,
[key]: value,
}), {});
例子:
. getelementbyid(“foobar”)。addEventListener('submit', (e) => { e.preventDefault (); const formData = new formData (e.target); const data = Array.from(formData.entries()).reduce((memo, [key, value]) => ({ 备忘录, (例子):值, }, {}); . getelementbyid(“输出”)。innerHTML = JSON.stringify(数据); }); <形式id =“foobar”> <input name='baz' /> <input type='submit' /> > < /形式 <pre id='output'>输入一些值并提交</pre>
尽管来自@dzuc的答案已经很好了,你可以使用数组解构(在现代浏览器或Babel中可用)使它更优雅:
// original version from @dzuc
const data = Array.from(formData.entries())
.reduce((memo, pair) => ({
...memo,
[pair[0]: pair[1],
}), {})
// with array destructuring
const data = Array.from(formData.entries())
.reduce((memo,[key, value]) => ({
...memo,
[key]: value,
}), {})
这篇文章已经有一年了……但是,我真的非常非常喜欢ES6 @dzuc的答案。然而,它是不完整的,不能够处理多个选择或复选框。已经指出了这一点,并提供了代码解决方案。我发现它们很笨重,而且没有优化。所以我写了两个基于@dzuc的版本来处理这些情况:
对于ASP样式的表单,多个项目名称可以简单重复。
let r=Array.from(fd).reduce(
(o , [k,v]) => (
(!o[k])
? {...o , [k] : v}
: {...o , [k] : [...o[k] , v]}
)
,{}
);
let obj=JSON.stringify(r);
一行高手版:
Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
对于PHP样式表单,其中多个项目名称必须有一个[]后缀。
let r=Array.from(fd).reduce(
(o , [k,v]) => (
(k.split('[').length>1)
? (k=k.split('[')[0]
, (!o[k])
? {...o , [k] : [v]}
: {...o , [k] : [...o[k] , v ]}
)
: {...o , [k] : v}
)
,{}
);
let obj=JSON.stringify(r);
一行高手版:
Array.from(fd).reduce((o,[k,v])=>((k.split('[').length>1)?(k=k.split('[')[0],(!o[k])?{...o,[k]:[v]}:{...o,[k]:[...o[k],v]}):{...o,[k]:v}),{});
支持多级数组的PHP表单扩展。
Since last time I wrote the previous second case, at work it came a case that the PHP form has checkboxes on multi-levels. I wrote a new case to support previous case and this one. I created a snippet to better showcase this case, the result show on the console for this demo, modify this to your need. Tried to optimize it the best I could without compromising performance, however, it compromise some human readability. It takes advantage that arrays are objects and variables pointing to arrays are kept as reference. No hotshot for this one, be my guest.
let nosubmit = (e) => { e.preventDefault(); const f = Array.from(new FormData(e.target)); const obj = f.reduce((o, [k, v]) => { let a = v, b, i, m = k.split('['), n = m[0], l = m.length; if (l > 1) { a = b = o[n] || []; for (i = 1; i < l; i++) { m[i] = (m[i].split(']')[0] || b.length) * 1; b = b[m[i]] = ((i + 1) == l) ? v : b[m[i]] || []; } } return { ...o, [n]: a }; }, {}); console.log(obj); } document.querySelector('#theform').addEventListener('submit', nosubmit, {capture: true}); <h1>Multilevel Form</h1> <form action="#" method="POST" enctype="multipart/form-data" id="theform"> <input type="hidden" name="_id" value="93242" /> <input type="hidden" name="_fid" value="45c0ec96929bc0d39a904ab5c7af70ef" /> <label>Select: <select name="uselect"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> </label> <br /><br /> <label>Checkboxes one level:<br/> <input name="c1[]" type="checkbox" checked value="1"/>v1 <input name="c1[]" type="checkbox" checked value="2"/>v2 <input name="c1[]" type="checkbox" checked value="3"/>v3 </label> <br /><br /> <label>Checkboxes two levels:<br/> <input name="c2[0][]" type="checkbox" checked value="4"/>0 v4 <input name="c2[0][]" type="checkbox" checked value="5"/>0 v5 <input name="c2[0][]" type="checkbox" checked value="6"/>0 v6 <br/> <input name="c2[1][]" type="checkbox" checked value="7"/>1 v7 <input name="c2[1][]" type="checkbox" checked value="8"/>1 v8 <input name="c2[1][]" type="checkbox" checked value="9"/>1 v9 </label> <br /><br /> <label>Radios: <input type="radio" name="uradio" value="yes">YES <input type="radio" name="uradio" checked value="no">NO </label> <br /><br /> <input type="submit" value="Submit" /> </form>
编辑:我看到已经有一个答案得出了非常相似的结果。
两个主要区别:
这不会将数值键作为数组下标,而是将它们视为对象键。可能不是正确的行为,但我个人从来没有写过使用这种符号的表单,可能会更新它。 这不是一个递归函数,这显然不是一个有利或不利的问题
忽略任何缺乏效率的问题。这将处理无限嵌套和数组的重复键。这显然不会转换文件之类的东西,但我不需要它,所以我没有添加它。
下面是一个JSFiddle的转换示例,可以用这个函数实现
一个很好的使用示例(这也是我的用例)是从html表单元素创建一个新的FormData对象,并轻松地将其转换为JSON进行发送。
/**
* @param {FormData} formData
* @return {Object}
*/
function formDataToObject(formData) {
const object = {};
for (let pair of formData.entries()) {
const key = pair[0];
const value = pair[1];
const isArray = key.endsWith('[]');
const name = key.substring(0, key.length - (2 * isArray));
const path = name.replaceAll(']', '');
const pathParts = path.split('[');
const partialsCount = pathParts.length;
let iterationObject = object;
for (let i = 0; i < partialsCount; i++) {
let part = pathParts[i];
let iterationObjectElement = iterationObject[part];
if (i !== partialsCount - 1) {
if (!iterationObject.hasOwnProperty(part) || typeof iterationObjectElement !== "object") {
iterationObject[part] = {};
}
iterationObject = iterationObject[part];
} else {
if (isArray) {
if (!iterationObject.hasOwnProperty(part)) {
iterationObject[part] = [value];
} else {
iterationObjectElement.push(value);
}
} else {
iterationObject[part] = value;
}
}
}
}
return object;
}
如果你想要json字符串:
var jsonStr = JSON.stringify($('#formId').serializeObject());
如果你想要一个json对象
var jsonObj = JSON.parse(JSON.stringify($('#formId').serializeObject()));