Як перетворити FormData (об’єкт HTML5) у JSON


104

Як перетворити об’єкт HTML5 FormData на JSON? Без Jquery та обробки вкладених властивостей у FormData як об’єкта.


2
Що ти намагаєшся робити? Чи JSON.stringify()допомагає? Можливо, ви намагаєтесь виправити щось, що може бути зроблено іншим способом?
Юстінас


3
Не є дублікатом, оскільки я не хочу перетворювати об’єкт javascript у json, а також не хочу використовувати Jquery.serialize ()
Леонардо Віллела,

Відповіді:


144

Ви також можете використовувати безпосередньо 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);

ОНОВЛЕННЯ 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:

Як згадувалося в одному з коментарів нижче моєї відповіді, stringifyметод JSON не спрацює не для всіх типів об'єктів. Для отримання додаткової інформації про те, які типи підтримуються, я хотів би звернутися до розділу "Опис" в документації MDNJSON.stringify .

В описі також згадується, що:

Якщо значення має метод toJSON (), він відповідає за те, які дані будуть серіалізовані.

Це означає, що ви можете забезпечити власний toJSONметод серіалізації логікою для серіалізації своїх користувацьких об'єктів. Подібно до цього ви можете швидко та легко створити підтримку серіалізації для більш складних дерев об’єктів.


1
Як зазначалося у відповіді від @TomasPrado, переконайтеся, що вам не потрібна підтримка IE11.
Уілт

4
Це не працює з елементами форми з декількома виділеннями, оскільки вони мають один і той же ключ, і в результаті ви перезаписуєте значення, що повертають лише останній вибраний елемент
Шон,

1
@Sean я дав відповідь , який працює з декількома значеннями <SELECT MULTIPLE>і <INPUT type="checkbox">з таким же ім'ям, шляхом перетворення значення в масив.
дещо

formData - це не серія. як ви можете повторити? прийнята відповідь, але щось пропущено.
Нурі ЙІЛЬМАЗ

4
Якщо не потрібні мультиселекти тощо. Відповідь JSON.stringify(Object.fromEntries(formData));набагато приємніша
Tom

108

У 2019 році таке завдання стало надзвичайно простим.

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

Object.fromEntries: Підтримується в Chrome 73+, Firefox 63+, Safari 12.1


2
Прийшов сюди, щоб опублікувати це, люблю дивитись на цю тему і бачити, як розвивалися відповіді протягом багатьох років
Марцін,

13
Здається, це не працює належним чином у формах, які мають кілька полів з однаковим іменем.
JukkaP

3
Зараз 2020 рік, і це не обробляє декілька вибраних значень <select multiple>або <input type="checkbox"> 😞
приблизно

4
Краще використовувати formData.entries:JSON.stringify(Object.fromEntries(formData.entries()));
Кохвер,

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, так як він не підтримує 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 );
  }

7

Ви можете досягти цього за допомогою об’єкта 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);

});

Це не виробляє json
Ліам

1
@Liam Ви пробували це з елементами форми? І дайте мені знати, чому він не створює об’єкт JSON?
GiriB

1
Не існує такого поняття, як json-об'єкт. Json - це нотна форма
Liam

1
@Liam Після створеного об'єкта вони можуть використовувати JSON.stringify (результат). І я відредагував свою відповідь. Будь ласка, перевірте це. І зняти голос проти.
GiriB

1
Ви також можете зробити висловлювання виразнішим, якщо використовуєте ES6: for (const [key, value] of formData.entries())
Тедді Цеттерлунд,

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');)

У цьому коді я створив порожню змінну JSON, використовуючи forцикл, я використовував keys від formData Object до ключів JSON у кожній Itration.

Ви знайдете цей код у моїй бібліотеці JS на GitHub. Запропонуйте мені, якщо це потребує вдосконалення. Я розмістив код тут https://github.com/alijamal14/Utilities/blob/master/Utilities.js


1
@zuluk пояснив спасибі
Алі Джамал

Це не обробляє декілька вибраних значень <select multiple>або <input type="checkbox">.
деякі

5

Цій публікації вже рік ... але, мені дуже, дуже подобається відповідь ES6 @dzuc. Однак він є неповним, оскільки не вдалося обробити декілька виділень або прапорців. Це вже вказано, і пропонуються кодові рішення. Я вважаю їх важкими та не оптимізованими. Тому я написав 2 версії на основі @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);

Версія Hotshot в один рядок:

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);

Версія 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, що підтримує багаторівневі масиви.

Оскільки минулого разу, коли я писав попередній другий випадок, під час роботи траплялося так, що форма 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}), {});hotshot версія es2018
nackjicholson

1
@nackjicholson Так, ти маєш рацію, опускаючи .entries () та елемент [k, v] спрощує код. Я перепишу код, щоб включити ці вдосконалення. Однак ваш все одно буде перезаписувати повторювані значення.
КарлосH.

3
Хоча я ціную зусилля - такий код абсурдний. Ніхто не хоче дивитись на літери змінних та об’єктів, це не 1985 рік.
Том

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

Якщо ви використовуєте лодаш, це можна зробити коротко fromPairs

import {fromPairs} from 'lodash';

const object = fromPairs(Array.from(formData.entries()));

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
Потрібно оновити значення "data" за замовчуванням з масиву "[]" до об'єкта "{}" в "функції оновлення (дані, ключі, значення) {" це видаляє порожню проблему масиву.
Чінтан Матхукія

Я також пропоную додати фільтр масиву перед зменшенням, щоб видалити порожні поля з кінцевого об’єкта
Ednilson Maia

@ChintanMathukiya Maia Чи можете ви поділитися зразком вводу, для якого ви отримуєте несподівані результати?
Джойс Бабу

@Ednilson Чи можете ви поділитися зразком даних, для яких ви бачите порожній вихідний масив?
Джойс Бабу,

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) у об’єкт key-> value like {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

У моєму випадку Дані були даними, fire base очікував об'єкт, але дані містять об'єкт, а також всі інші матеріали, тому я спробував 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;
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.