如何将FormData(HTML5对象)转换为JSON


104

如何将HTML5 FormData对象转换为JSON?没有Jquery并像对象一样处理FormData中的嵌套属性。


2
你想做什么?有JSON.stringify()帮助吗?也许您尝试解决其他方法可能会解决的问题?
Justinas


3
不是重复的,因为我不想将JavaScript对象转换为json,也不想使用Jquery.serialize()
Leonardo

Answers:


144

您也可以直接forEachFormData对象上使用:

var object = {};
formData.forEach(function(value, key){
    object[key] = value;
});
var json = JSON.stringify(object);

更新:

对于喜欢使用ES6箭头功能的相同解决方案的用户:

var object = {};
formData.forEach((value, key) => {object[key] = value});
var json = JSON.stringify(object);

更新2:

对于那些想要支持具有多个值的多重选择列表或其他表单元素的人(由于在此问题的答案下方有太多注释,我将添加一个可能的解决方案)

var object = {};
formData.forEach((value, key) => {
    // Reflect.has in favor of: object.hasOwnProperty(key)
    if(!Reflect.has(object, key)){
        object[key] = value;
        return;
    }
    if(!Array.isArray(object[key])){
        object[key] = [object[key]];    
    }
    object[key].push(value);
});
var json = JSON.stringify(object);

这里的小提琴通过简单的多选择列表演示了此方法的使用。

更新3:

对于那些到此为止的人来说,如果将表单数据转换为json的目的是通过XML HTTP请求将其发送到服务器,则可以FormData直接发送对象而无需进行转换。就这么简单:

var request = new XMLHttpRequest();
request.open("POST", "http://example.com/submitform.php");
request.send(formData);

使用FORMDATA对象上MDN以供参考

更新4:

如下面我的回答中的评论之一所述,JSON stringify方法不适用于所有类型的对象。有关支持哪种类型的更多信息,我想参考的MDN文档中的“描述”部分JSON.stringify

在说明中还提到:

如果该值具有toJSON()方法,则负责定义将序列化哪些数据。

这意味着您可以为自己的toJSON序列化方法提供用于序列化自定义对象的逻辑。这样,您可以快速轻松地为更复杂的对象树建立序列化支持。


1
如@TomasPrado的答案中所述,请确保您不需要IE11的支持。
威尔特

4
这不适用于多重选择表单元素,因为它们共享相同的键,最终您覆盖了仅返回最后一个选定元素的值
Sean

1
@Sean我给了一个答案,与多个值的作品<SELECT MULTIPLE>,并<INPUT type="checkbox">用相同的名称,由值转换为数组。
一些

formData不是一个序列。您如何迭代?接受了答案,但是错过了一些东西。
Nuri YILMAZ

4
除非需要多重选择等等。答案JSON.stringify(Object.fromEntries(formData));就更好了
Tom Stickel

108

在2019年,这种任务变得异常简单。

JSON.stringify(Object.fromEntries(formData));

Object.fromEntries:Chrome 73 +,Firefox 63 +,Safari 12.1中受支持


2
来到这里发表这篇文章,喜欢看这个话题,看看这些年来答案如何演变
Marcin

13
在具有多个具有相同名称的字段的表单上,这似乎无法正常工作。
JukkaP

3
现在是2020年,这不会处理<select multiple><input type="checkbox"> multiple的多个选定值
一些

4
更好地使用formData.entries:JSON.stringify(Object.fromEntries(formData.entries()));
Kohver,

22

这是一种无需使用库即可以更具功能性的样式进行操作的方法。

Array.from(formData.entries()).reduce((memo, pair) => ({
  ...memo,
  [pair[0]]: pair[1],
}), {});

例:

document.getElementById('foobar').addEventListener('submit', (e) => {
  e.preventDefault();

  const formData = new FormData(e.target);
  const data = Array.from(formData.entries()).reduce((memo, pair) => ({
    ...memo,
    [pair[0]]: pair[1],
  }), {});
  document.getElementById('output').innerHTML = JSON.stringify(data);
});
<form id='foobar'>
  <input name='baz' />
  <input type='submit' />
</form>

<pre id='output'>Input some value and submit</pre>


1
最好的答案在这里。谢谢:)
Chunky Chunk

4
我真的很喜欢这个答案,但仍然无法处理多个项目。我根据这个答案发布了一个新的答案来处理这些情况。
卡洛斯·H。

9

如果您有多个具有相同名称的条目,例如,如果您使用<SELECT multiple>多个或具有相同名称的条目,则<INPUT type="checkbox">需要注意这一点并制作一个值数组。否则,您只会得到最后选择的值。

这是现代的ES6变体:

function formToJSON( elem ) {
  let output = {};
  new FormData( elem ).forEach(
    ( value, key ) => {
      // Check if property already exist
      if ( Object.prototype.hasOwnProperty.call( output, key ) ) {
        let current = output[ key ];
        if ( !Array.isArray( current ) ) {
          // If it's not an array, convert it to an array.
          current = output[ key ] = [ current ];
        }
        current.push( value ); // Add the new value to the array.
      } else {
        output[ key ] = value;
      }
    }
  );
  return JSON.stringify( output );
}

稍旧代码(但是仍然不支持IE11,因为它不支持ForEachentriesFormData

function formToJSON( elem ) {
  var current, entries, item, key, output, value;
  output = {};
  entries = new FormData( elem ).entries();
  // Iterate over values, and assign to item.
  while ( item = entries.next().value )
    {
      // assign to variables to make the code more readable.
      key = item[0];
      value = item[1];
      // Check if key already exist
      if (Object.prototype.hasOwnProperty.call( output, key)) {
        current = output[ key ];
        if ( !Array.isArray( current ) ) {
          // If it's not an array, convert it to an array.
          current = output[ key ] = [ current ];
        }
        current.push( value ); // Add the new value to the array.
      } else {
        output[ key ] = value;
      }
    }
    return JSON.stringify( output );
  }

7

您可以通过使用FormData()对象来实现。将使用每个键的元素的name属性及其值的提交值,使用该窗体的当前键/值填充此FormData对象。它还将对文件输入内容进行编码。

例:

var myForm = document.getElementById('myForm');
myForm.addEventListener('submit', function(event)
{
    event.preventDefault();
    var formData = new FormData(myForm),
        result = {};

    for (var entry of formData.entries())
    {
        result[entry[0]] = entry[1];
    }
    result = JSON.stringify(result)
    console.log(result);

});

这不会产生json
Liam

1
@Liam您是否尝试过使用表单元素?让我知道为什么它不产生JSON对象?
GiriB

1
没有像json对象这样的东西。杰森(Json)是一个字符串符号
Liam,

1
@Liam创建对象后,他们可以使用JSON.stringify(result)。而且我已经编辑了答案。请检查一下。并撤回反对票。
GiriB

1
如果您使用ES6,也可以使for语句的表达更具表现力: for (const [key, value] of formData.entries())
Teddy Zetterlund

7

易于使用的功能

为此创建了一个函数

function FormDataToJSON(FormElement){    
    var formData = new FormData(FormElement);
    var ConvertedJSON= {};
    for (const [key, value]  of formData.entries())
    {
        ConvertedJSON[key] = value;
    }

    return ConvertedJSON
}

用法示例

var ReceivedJSON = FormDataToJSON(document.getElementById('FormId');)

在这段代码中,我使用for循环创建了一个空的JSON变量key在每个Itration中从formData对象到JSON键的。

您可以在GitHub上的JS库中找到此代码,如果需要改进,请建议我将代码放在这里https://github.com/alijamal14/Utilities/blob/master/Utilities.js


1
@zuluk 解释 谢谢你
Ali Jamal

这不能处理<select multiple>或选择的多个值<input type="checkbox">
一些

5

这篇文章已经有一年历史了,但是,我真的非常喜欢ES6 @dzuc的答案。但是,由于无法处理多个选择或复选框,因此不完整。这已经指出并提供了代码解决方案。我发现它们很重且没有优化。因此,我基于@dzuc编写了2个版本来处理这些情况:

  • 对于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表单扩展。

自从我上次写上一个第二个案例以来,在工作中出现了一个PHP表单具有多层复选框的情况。我写了一个新案例来支持以前的案例和这个案例。我创建了一个片段以更好地展示这种情况,此演示的结果显示在控制台上,并根据需要进行修改。试图在不影响性能的情况下尽最大可能对其进行优化,但是,它会损害某些人类可读性。利用数组是对象的优势,并且将指向数组的变量保留为引用。没问题,请成为我的客人。

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>


2
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});es2018热门版本
nackjicholson

1
@nackjicholson是的,您是对的,省略了.entries()和[k,v]元素简化了代码。我将重写代码以包括这些改进。但是,您的仍然会覆盖重复的值。
卡洛斯·H。

3
尽管我很感激,但这样的代码是荒谬的。没人想看字母来表示变量和对象,这不是1985
。– Tom Stickel

4

FormData方法.entries不支持for of表达式IE11和Safari。

这是一个更简单的版本,支持Safari,Chrome,Firefox和Edge

function formDataToJSON(formElement) {    
    var formData = new FormData(formElement),
        convertedJSON = {};

    formData.forEach(function(value, key) { 
        convertedJSON[key] = value;
    });

    return convertedJSON;
}

警告:此答案在IE11中不起作用。
FormData forEach在IE11 中没有方法。
我仍在寻找支持所有主流浏览器的最终解决方案。


太棒了!我们必须支持较旧的浏览器,并且迭代器的使用不是很直观。
彼得·霍金斯


3

如果您需要序列化嵌套字段的支持(类似于PHP处理表单字段的方式),则可以使用以下函数

function update(data, keys, value) {
  if (keys.length === 0) {
    // Leaf node
    return value;
  }

  let key = keys.shift();
  if (!key) {
    data = data || [];
    if (Array.isArray(data)) {
      key = data.length;
    }
  }

  // Try converting key to a numeric value
  let index = +key;
  if (!isNaN(index)) {
    // We have a numeric index, make data a numeric array
    // This will not work if this is a associative array 
    // with numeric keys
    data = data || [];
    key = index;
  }
  
  // If none of the above matched, we have an associative array
  data = data || {};

  let val = update(data[key], keys, value);
  data[key] = val;

  return data;
}

function serializeForm(form) {
  return Array.from((new FormData(form)).entries())
    .reduce((data, [field, value]) => {
      let [_, prefix, keys] = field.match(/^([^\[]+)((?:\[[^\]]*\])*)/);

      if (keys) {
        keys = Array.from(keys.matchAll(/\[([^\]]*)\]/g), m => m[1]);
        value = update(data[prefix], keys, value);
      }
      data[prefix] = value;
      return data;
    }, {});
}

document.getElementById('output').textContent = JSON.stringify(serializeForm(document.getElementById('form')), null, 2);
<form id="form">
  <input name="field1" value="Field 1">
  <input name="field2[]" value="Field 21">
  <input name="field2[]" value="Field 22">
  <input name="field3[a]" value="Field 3a">
  <input name="field3[b]" value="Field 3b">
  <input name="field3[c]" value="Field 3c">
  <input name="field4[x][a]" value="Field xa">
  <input name="field4[x][b]" value="Field xb">
  <input name="field4[x][c]" value="Field xc">
  <input name="field4[y][a]" value="Field ya">
  <input name="field5[z][0]" value="Field z0">
  <input name="field5[z][]" value="Field z1">
  <input name="field6.z" value="Field 6Z0">
  <input name="field6.z" value="Field 6Z1">
</form>

<h2>Output</h2>
<pre id="output">
</pre>


1
需要将“数据”的默认值从数组“ []”更新为“函数更新(数据,键,值){”中的对象“ {}”,从而消除了空白数组问题。
Chintan Mathukiya

我还建议在减少之前将数组过滤器添加到最终对象中以删除空字段
Ednilson Maia

@ChintanMathukiya Maia您可以共享您有意外输出的示例输入吗?
Joyce Babu

@Ednilson您可以共享看到空数组输出的示例数据吗?
Joyce Babu

2

你可以试试这个

formDataToJSON($('#form_example'));

# Create a function to convert the serialize and convert the form data
# to JSON
# @param : $('#form_example');
# @return a JSON Stringify
function formDataToJSON(form) {
    let obj = {};
    let formData = form.serialize();
    let formArray = formData.split("&");

    for (inputData of formArray){
        let dataTmp = inputData.split('=');
        obj[dataTmp[0]] = dataTmp[1];
    }
    return JSON.stringify(obj);
}

2

即使@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,
  }), {})

2

辱骂一线!

Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});

今天,我了解到firefox具有对象传播支持和数组销毁功能!


1

如果以下各项满足您的需求,那么您很幸运:

  1. 您想将数组[['key','value1'], ['key2','value2'](如FormData给您的数组)转换成键-值对象(如{key1: 'value1', key2: 'value2'},然后将其转换为JSON字符串。
  2. 您正在使用最新的ES6解释器来定位浏览器/设备,或者正在使用babel之类的东西进行编译。
  3. 您想要最简单的方法来完成此任务。

这是您需要的代码:

const data = new FormData(document.querySelector('form'));
const json = JSON.stringify(Array.from(data).reduce((o,[k,v])=>(o[k]=v,o),{}));

希望这对某人有帮助。


1

我还没有提到FormData.getAll到目前为止,方法。

除了从FormData对象中返回与给定键相关的所有值之外,使用此处其他人指定的Object.fromEntries方法也变得非常简单。

var formData = new FormData(document.forms[0])

var obj = Object.fromEntries(
  Array.from(formData.keys()).map(key => [
    key, formData.getAll(key).length > 1 
    ? formData.getAll(key)
    : formData.get(key)
  ])
)

片段在行动

var formData = new FormData(document.forms[0])

var obj = Object.fromEntries(Array.from(formData.keys()).map(key => [key, formData.getAll(key).length > 1 ? formData.getAll(key) : formData.get(key)]))

document.write(`<pre>${JSON.stringify(obj)}</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>


0

为我工作

                var myForm = document.getElementById("form");
                var formData = new FormData(myForm),
                obj = {};
                for (var entry of formData.entries()){
                    obj[entry[0]] = entry[1];
                }
                console.log(obj);

它不会处理<select multiple>或的多个选定值<input type="checkbox">
某些

0

在我的情况下,数据是data,fire base期望有一个对象,但是data包含对象以及所有其他东西,所以我尝试使用data.value可以正常工作!


0

我到这儿很晚。但是,我做了一个简单的方法来检查输入的类型=“ checkbox”

var formData = new FormData($form.get(0));
        var objectData = {};
        formData.forEach(function (value, key) {
            var updatedValue = value;
            if ($('input[name="' + key + '"]').attr("type") === "checkbox" && $('input[name="' + key + '"]').is(":checked")) {
                updatedValue = true; // we don't set false due to it is by default on HTML
            }
            objectData[key] = updatedValue;
        });
var jsonData = JSON.stringify(objectData);

我希望这对其他人有帮助。


-1

您无需任何特殊操作即可轻松完成此工作。像下面这样的代码就足够了。

var form = $(e.currentTarget);

var formData = objectifyForm(form);


function objectifyForm(formArray) {

    var returnArray = {};
    for (var i = 0; i < formArray[0].length; i++) {
        var name = formArray[0][i]['name'];
        if (name == "") continue;
        if (formArray[0][i]['type'] == "hidden" && returnArray[name] != undefined) continue;
        if ($(formArray[0][i]).attr("type") == "radio") {
            var radioInputs = $("[name='" + name + "']");
            var value = null;
            radioInputs.each(function (radio) {
                if ($(this)[0].checked == true) {
                    value = $(this).attr("id").split("_")[$(this).attr("id").split("_").length - 1];
                }
            });
            returnArray[name] = value;
        }
        else if ($(formArray[0][i]).attr("type") == "checkbox") {
            returnArray[name] = $(formArray[0][i])[0].checked;
        }
        else
            returnArray[name] = formArray[0][i]['value'];
    }



    return returnArray;
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.