Jak przekonwertować FormData (obiekt HTML5) na JSON

114

Jak przekonwertować obiekt FormData HTML5 na JSON? Bez JQuery i obsługi zagnieżdżonych właściwości w FormData jak obiekt.

Leonardo Villela
źródło
2
Co próbujesz zrobić? Czy JSON.stringify()pomaga? Może próbujesz naprawić coś, co można zrobić w inny sposób?
Justinas
Możliwy duplikat obiektu Convert JS na string JSON
Liam
4
Czy nie powielać, ponieważ nie chcę konwertować obiekt javascript do JSON, ani nie chcę używać Jquery.serialize ()
Leonardo Villela
sprawdź to: stackoverflow.com/a/39248551/6293856
Bhavik Hirani

Odpowiedzi:

161

Możesz również użyć bezpośrednio forEachna FormDataobiekcie:

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

AKTUALIZACJA:

A dla tych, którzy wolą to samo rozwiązanie z funkcjami strzałek ES6 :

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

AKTUALIZACJA 2:

A dla tych, którzy chcą obsługiwać listy wielokrotnego wyboru lub inne elementy formularza z wieloma wartościami (ponieważ pod odpowiedzią na ten temat jest tak wiele komentarzy, dodam możliwe rozwiązanie) :

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

Tutaj Fiddle demonstruje użycie tej metody z prostą listą wielokrotnego wyboru.

AKTUALIZACJA 3:

Na marginesie dla tych, którzy kończą w tym miejscu, w przypadku, gdy celem konwersji danych formularza na json jest wysłanie ich przez żądanie XML HTTP do serwera, można wysłać FormDataobiekt bezpośrednio, bez konwersji. Tak proste, jak to:

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

Zobacz także Korzystanie z obiektów FormData w MDN :

AKTUALIZACJA 4:

Jak wspomniano w jednym z komentarzy poniżej, moja odpowiedź, stringifymetoda JSON nie będzie działać po wyjęciu z pudełka dla wszystkich typów obiektów. Aby uzyskać więcej informacji na temat obsługiwanych typów, zapoznaj się z sekcją Opis w dokumentacji MDNJSON.stringify .

W opisie wspomina się również, że:

Jeśli wartość ma metodę toJSON (), jest odpowiedzialna za określenie, jakie dane będą serializowane.

Oznacza to, że możesz podać własną toJSONmetodę serializacji z logiką do serializacji obiektów niestandardowych. W ten sposób można szybko i łatwo utworzyć obsługę serializacji dla bardziej złożonych drzew obiektów.

Więdnąć
źródło
1
Jak wspomniano w odpowiedzi od @TomasPrado, upewnij się, że nie potrzebujesz wsparcia dla IE11.
Wilt
4
Nie działa to w przypadku elementów formularza wielokrotnego wyboru, ponieważ mają one ten sam klucz, co kończy się nadpisywaniem wartości zwracających tylko ostatni wybrany element
Sean
1
@Sean Podałem odpowiedź, która działa z wieloma wartościami dla <SELECT MULTIPLE>io tej <INPUT type="checkbox">samej nazwie, konwertując wartość na tablicę.
jakiś
formData nie jest serią. jak możesz iterować? zaakceptowana odpowiedź, ale coś przeoczyło.
Nuri YILMAZ
5
Chyba że potrzeba wielu selekcji itp. Odpowiedź JSON.stringify(Object.fromEntries(formData));jest o wiele przyjemniejsza
Tom Stickel
114

W 2019 roku tego rodzaju zadanie stało się super łatwe.

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

Object.fromEntries: Obsługiwane w Chrome 73+, Firefox 63+, Safari 12.1

hakatashi
źródło
2
Przyszedłem tutaj, aby to opublikować, uwielbiam patrzeć na ten wątek i widzieć, jak odpowiedzi ewoluowały na przestrzeni lat
Marcin
14
Wydaje się, że nie działa to poprawnie w formularzach, które mają wiele pól o tej samej nazwie.
JukkaP
3
Jest rok 2020 i to nie obsługuje wielu wybranych wartości <select multiple>lub <input type="checkbox"> 😞
około
4
Lepiej użyć formData.entries:JSON.stringify(Object.fromEntries(formData.entries()));
Kohver
22

Oto sposób na zrobienie tego w bardziej funkcjonalnym stylu, bez korzystania z biblioteki.

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

Przykład:

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>

dzuc
źródło
1
Najlepsza odpowiedź tutaj. Dzięki :)
Chunky Chunk
4
Bardzo podoba mi się ta odpowiedź, ale nadal nie obsługuję wielu pozycji. Opublikowałem nową odpowiedź opartą na tej, aby obsłużyć te przypadki.
CarlosH.
10

Jeśli masz wiele wpisów o tej samej nazwie, na przykład jeśli używasz <SELECT multiple>lub masz wiele wpisów o tej samej nazwie, <INPUT type="checkbox">musisz się tym zająć i utworzyć tablicę wartości. W przeciwnym razie otrzymasz tylko ostatnią wybraną wartość.

Oto nowoczesny wariant 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 );
}

Nieco starszy kod (ale nadal nie jest obsługiwany przez IE11, ponieważ nie obsługuje ForEachani nie jest entrieswłączony 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 );
  }
trochę
źródło
7

Można to osiągnąć za pomocą obiektu FormData () . Ten obiekt FormData zostanie wypełniony bieżącymi kluczami / wartościami formularza przy użyciu właściwości name każdego elementu dla kluczy i ich przesłanej wartości dla wartości. Zakoduje również zawartość wejściową pliku.

Przykład:

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

});
GiriB
źródło
To nie produkuje json
Liam
1
@Liam Czy próbowałeś tego z elementami formularza? I daj mi znać, dlaczego nie tworzy obiektu JSON?
GiriB,
1
Nie ma czegoś takiego jak obiekt json. Json to notacja typu string
Liam,
1
@Liam Po utworzeniu obiektu mogą używać JSON.stringify (wynik). I zredagowałem odpowiedź. Proszę sprawdź to. I wycofaj głos przeciwny.
GiriB,
1
Możesz również uczynić for of bardziej wyrazistym, jeśli używasz ES6: for (const [key, value] of formData.entries())
Teddy Zetterlund
7

Łatwa w użyciu funkcja

Stworzyłem do tego funkcję

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

    return ConvertedJSON
}

Przykładowe użycie

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

W tym kodzie utworzyłem pustą zmienną JSON za pomocą forpętli. Użyłem keys z obiektu formData do kluczy JSON w każdym Itration.

Ten kod znajdujesz w mojej bibliotece JS na GitHubie, zasugeruj mi, jeśli wymaga poprawy Umieściłem kod tutaj https://github.com/alijamal14/Utilities/blob/master/Utilities.js

Ali Jamal
źródło
1
@zuluk Wyjaśnione Dziękuję
Ali Jamal
To nie obsługuje wielu wybranych wartości <select multiple>lub <input type="checkbox">.
jakiś
5

Ten post ma już rok ... ale naprawdę podoba mi się odpowiedź @dzuc w ES6. Jednak jest niekompletny, ponieważ nie był w stanie obsłużyć wielu wyborów lub pól wyboru. To już wskazywało i oferowano rozwiązania kodowe. Uważam, że są ciężkie i niezoptymalizowane. Napisałem więc 2 wersje w oparciu o @dzuc do obsługi tych przypadków:

  • Dla formularzy w stylu ASP, w których nazwy wielu elementów można po prostu powtórzyć.
let r=Array.from(fd).reduce(
  (o , [k,v]) => (
     (!o[k])
     ? {...o , [k] : v}
     : {...o , [k] : [...o[k] , v]}
   )
   ,{}
);
let obj=JSON.stringify(r);

Wersja One line Hotshot:

Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
  • Dla formularzy w stylu PHP, gdzie nazwy wielu elementów muszą mieć []sufiks.
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);

Wersja One line 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}),{});
  • Rozszerzenie formularza PHP obsługującego tablice wielopoziomowe.

Od czasu, gdy ostatnio pisałem poprzedni drugi przypadek, w pracy zdarzyło się, że formularz PHP ma checkboxy na wielu poziomach. Napisałem nową sprawę, aby wspierać poprzednią sprawę i tę. Stworzyłem fragment, aby lepiej zaprezentować ten przypadek, wynik pokaż na konsoli dla tego demo, zmodyfikuj go do swoich potrzeb. Próbowałem zoptymalizować go najlepiej, jak mogłem, bez uszczerbku dla wydajności, jednak pogorszyło to czytelność dla ludzi. Wykorzystuje to, że tablice są obiektami, a zmienne wskazujące na tablice są zachowywane jako odniesienie. Nie ma gorącej odpowiedzi na to, bądź moim gościem.

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>

CarlosH.
źródło
2
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});wersja hotshot es2018
nackjicholson
1
@nackjicholson Yeap, masz rację, pomijając .entries () i element [k, v] upraszczają kod. Przepiszę kod, aby uwzględnić te ulepszenia. Jednak Twoje nadal będą nadpisywać powtarzające się wartości.
CarlosH.
3
Chociaż doceniam ten wysiłek, taki kod jest absurdalny. Nikt nie chce patrzeć na litery dla zmiennych i obiektów, to nie jest rok 1985.
Tom Stickel
4

FormData .entriesi for ofwyrażenie nie są obsługiwane w przeglądarkach IE11 i Safari.

Oto prostsza wersja obsługująca Safari, Chrome, Firefox i Edge

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

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

    return convertedJSON;
}

Ostrzeżenie: ta odpowiedź nie działa w IE11.
FormData nie ma forEachmetody w IE11.
Wciąż szukam ostatecznego rozwiązania obsługującego wszystkie główne przeglądarki.

Tomas Prado
źródło
to jest doskonałe! musimy obsługiwać starsze przeglądarki, a użycie iteratora nie jest zbyt intuicyjne.
Peter Hawkins
4

Jeśli potrzebujesz wsparcia dla serializacji pól zagnieżdżonych, podobnie jak PHP obsługuje pola formularzy, możesz użyć następującej funkcji

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>

Joyce Babu
źródło
1
Trzeba zaktualizować domyślną wartość "danych" z tablicy "[]" do obiektu "{}" w "aktualizacja funkcji" (dane, klucze, wartość) {"usuwa problem z pustą tablicą.
Chintan Mathukiya
Proponuję również dodać filtr tablicowy przed redukcją, aby usunąć puste pola z końcowego obiektu
Ednilson Maia
@ChintanMathukiya Maia Czy możesz udostępnić przykładowe wejście, dla którego masz nieoczekiwane wyniki?
Joyce Babu
@Ednilson Czy możesz udostępnić przykładowe dane, dla których widzisz puste wyjście tablicy?
Joyce Babu
3

Jeśli używasz lodash, można to zrobić zwięźle fromPairs

import {fromPairs} from 'lodash';

const object = fromPairs(Array.from(formData.entries()));
Erik van Velzen
źródło
2

Możesz tego spróbować

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);
}
Ivan Fretes
źródło
2

Chociaż odpowiedź od @dzuc jest już bardzo dobra, możesz użyć destrukturyzacji tablic (dostępne w nowoczesnych przeglądarkach lub z Babel), aby uczynić ją jeszcze bardziej elegancką:

// 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,
  }), {})
Jeremias Erbs
źródło
2

Obraźliwa jedna linijka!

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

Dzisiaj dowiedziałem się, że Firefox obsługuje rozprzestrzenianie obiektów i niszczenie tablic!

nackjicholson
źródło
1

Jeśli poniższe elementy spełniają Twoje potrzeby, masz szczęście:

  1. Chcesz przekonwertować tablicę tablic, takich jak [['key','value1'], ['key2','value2'](na przykład to, co daje FormData) na obiekt klucz-> wartość, taki jak {key1: 'value1', key2: 'value2'}i przekonwertować go na ciąg JSON.
  2. Wybierasz przeglądarki / urządzenia z najnowszym interpreterem ES6 lub kompilujesz coś takiego jak babel.
  3. Chcesz najmniejszego sposobu, aby to osiągnąć.

Oto kod, którego będziesz potrzebować:

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

Mam nadzieję, że to komuś pomoże.

KyleFarris
źródło
1

Jak dotąd nie widziałem wzmianek o metodzie FormData.getAll .

Oprócz zwracania wszystkich wartości skojarzonych z danym kluczem z obiektu FormData, staje się to naprawdę proste przy użyciu metody Object.fromEntries określonej przez innych tutaj.

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

Fragment w akcji

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>

OpSocket
źródło
1

Myślę, że jest to najprostszy sposób na uzyskanie pożądanego wyniku z formDataobiektu FormData:

const jsonData = {};

for(const [key, value] of formData) {
    jsonData[key] = value;
}
Czerwony Krow
źródło
0

Pracował dla mnie

                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);
Shahid Hussain Abbasi
źródło
Nie będzie obsługiwał wielu wybranych wartości <select multiple>lub<input type="checkbox">
niektóre
0

W moim przypadku forma Data była danymi, baza ognia oczekiwała obiektu, ale dane zawierają obiekt i wszystkie inne rzeczy, więc spróbowałem data.value to zadziałało !!!

Rahul solanki
źródło
0

Spóźniam się tutaj. Jednak stworzyłem prostą metodę, która sprawdza typ wejściowy = "pole wyboru"

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

Mam nadzieję, że to pomoże komuś innemu.

acido
źródło
-1

Możesz łatwo wykonać tę pracę, nie używając niczego specjalnego. Wystarczy kod podobny do poniższego.

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;
};
nercan
źródło