Как преобразовать объект HTML5 FormData в JSON? Без Jquery и обработки вложенных свойств в FormData как объекта.
Как преобразовать объект HTML5 FormData в JSON? Без Jquery и обработки вложенных свойств в FormData как объекта.
Ответы:
Вы также можете использовать непосредственно forEach
на FormData
объекте:
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);
А для тех, кому нужна поддержка списков с множественным выбором или других элементов формы с несколькими значениями (поскольку под ответом по этой проблеме так много комментариев, я добавлю возможное решение) :
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);
Здесь Fiddle демонстрирует использование этого метода с простым списком множественного выбора.
В качестве побочного примечания для тех, кто здесь оказался, в случае, если целью преобразования данных формы в json является их отправка через HTTP-запрос XML на сервер, вы можете отправить FormData
объект напрямую, не преобразовывая его. Вот так просто:
var request = new XMLHttpRequest();
request.open("POST", "http://example.com/submitform.php");
request.send(formData);
См. Также Использование объектов FormData в MDN для справки :
Как упоминалось в одном из комментариев ниже моего ответа, stringify
метод JSON не будет работать из коробки для всех типов объектов. Для получения дополнительной информации о том, какие типы поддерживаются, я хотел бы обратиться к разделу Описание в документации MDNJSON.stringify
.
В описании также упоминается, что:
Если значение имеет метод toJSON (), он отвечает за определение того, какие данные будут сериализованы.
Это означает, что вы можете предоставить свой собственный toJSON
метод сериализации с логикой для сериализации ваших пользовательских объектов. Таким образом вы можете быстро и легко создать поддержку сериализации для более сложных деревьев объектов.
<SELECT MULTIPLE>
и того <INPUT type="checkbox">
же имени, путем преобразования значения в массив.
JSON.stringify(Object.fromEntries(formData));
будет намного лучше
В 2019 году такая задача стала сверхлегкой.
JSON.stringify(Object.fromEntries(formData));
Object.fromEntries
: Поддерживается в Chrome 73+, Firefox 63+, Safari 12.1.
<select multiple>
или <input type="checkbox">
😞
JSON.stringify(Object.fromEntries(formData.entries()));
Вот способ сделать это в более функциональном стиле без использования библиотеки.
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>
Если у вас есть несколько записей с одним и тем же именем, например, если вы используете <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, так как он не поддерживает ForEach
или entries
на FormData
)
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 );
}
Вы можете добиться этого с помощью объекта FormData () . Этот объект FormData будет заполнен текущими ключами / значениями формы с использованием свойства name каждого элемента для ключей и их отправленного значения для значений. Он также будет кодировать входное содержимое файла.
Пример:
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);
});
for (const [key, value] of formData.entries())
Простая в использовании функция
Я создал для этого функцию
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');)
В этом коде я создал пустую переменную JSON, используя for
цикл, который я использовалkey
от объекта formData до ключей JSON в каждой итерации.
Вы найдете этот код в моей библиотеке JS на GitHub. Предложите мне, если он нуждается в улучшении. Я разместил код здесь https://github.com/alijamal14/Utilities/blob/master/Utilities.js
<select multiple>
или <input type="checkbox">
.
Этому посту уже год ... но мне очень нравится ответ ES6 @dzuc. Однако он неполный, поскольку не может обрабатывать несколько вариантов выбора или флажков. На это уже указывалось, и предлагались кодовые решения. Я считаю их тяжелыми и не оптимизированными. Поэтому я написал 2 версии на основе @dzuc для обработки этих случаев:
let r=Array.from(fd).reduce(
(o , [k,v]) => (
(!o[k])
? {...o , [k] : v}
: {...o , [k] : [...o[k] , v]}
)
,{}
);
let obj=JSON.stringify(r);
Однострочная версия Hotshot:
Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
[]
суффикс.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);
Однострочная версия Hotshot:
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 есть флажки на многоуровневых уровнях. Я написал новый случай, чтобы подтвердить предыдущий и этот. Я создал фрагмент, чтобы лучше продемонстрировать этот случай, результат отображается на консоли для этой демонстрации, измените его по своему усмотрению. Пытался оптимизировать его, насколько мог, без ущерба для производительности, однако он в некоторой степени ухудшил удобочитаемость. Преимущество заключается в том, что массивы являются объектами, а переменные, указывающие на массивы, хранятся как ссылки. Нет уж, будь моим гостем.
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>
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});
hotshot version es2018
Метод 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.
Я все еще ищу окончательное решение для поддержки всех основных браузеров.
Если вам нужна поддержка для сериализации вложенных полей, аналогично тому, как 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>
Если вы используете lodash, это можно сделать кратко с помощью fromPairs
import {fromPairs} from 'lodash';
const object = fromPairs(Array.from(formData.entries()));
Вы можете попробовать это
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);
}
Несмотря на то, что ответ от @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,
}), {})
Оскорбительная однострочка!
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});
Сегодня я узнал, что firefox поддерживает распространение объектов и деструктуризацию массивов!
Если следующие предметы соответствуют вашим потребностям, вам повезло:
[['key','value1'], ['key2','value2']
(например, то, что дает вам FormData), в объект key-> value, например{key1: 'value1', key2: 'value2'}
и преобразовать его в строку JSON.Вот код, который вам понадобится:
const data = new FormData(document.querySelector('form'));
const json = JSON.stringify(Array.from(data).reduce((o,[k,v])=>(o[k]=v,o),{}));
Надеюсь, это кому-то поможет.
Я не видел упоминаний о 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>
Я думаю, что это самый простой способ получить formData
желаемый результат от объекта FormData:
const jsonData = {};
for(const [key, value] of formData) {
jsonData[key] = value;
}
Работал на меня
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">
В моем случае форма Data была данными, база огня ожидала объект, но данные содержат объект, а также все другие материалы, поэтому я попробовал data.value, это сработало !!!
Я опаздываю сюда. Однако я сделал простой метод, который проверяет тип ввода = "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);
Надеюсь, это поможет кому-то другому.
Вы можете легко выполнить эту работу, не используя ничего особенного. Код, подобный приведенному ниже, будет достаточно.
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;
};
JSON.stringify()
? Может ты попробуешь исправить то, что может быть сделано другим способом?