Come convertire FormData (oggetto HTML5) in JSON


104

Come convertire l'oggetto FormData HTML5 in JSON? Senza Jquery e la gestione delle proprietà nidificate in FormData come un oggetto.


2
Cosa stai cercando di fare? Non JSON.stringify()aiuta? Forse cerchi di aggiustare qualcosa che può essere fatto in altro modo?
Justinas

Possibile duplicato di Converti oggetto JS in stringa JSON
Liam

3
Non è duplicato poiché non voglio convertire un oggetto javascript in un json, né voglio usare Jquery.serialize ()
Leonardo Villela

Risposte:


145

Si potrebbe anche usare forEachsul FormDataoggetto direttamente:

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

AGGIORNARE:

E per chi preferisce la stessa soluzione con le funzioni freccia ES6 :

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

AGGIORNAMENTO 2:

E per coloro che desiderano il supporto per elenchi di selezione multipla o altri elementi del modulo con più valori (poiché ci sono così tanti commenti sotto la risposta riguardo a questo problema, aggiungerò una possibile soluzione) :

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

Qui un violino che dimostra l'uso di questo metodo con un semplice elenco di selezione multipla.

AGGIORNAMENTO 3:

Come nota a margine per coloro che finiscono qui, nel caso in cui lo scopo della conversione dei dati del modulo in json sia di inviarlo tramite una richiesta HTTP XML a un server, è possibile inviare l' FormDataoggetto direttamente senza convertirlo. Semplice come questo:

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

Vedere anche Utilizzo di oggetti FormData su MDN per riferimento :

AGGIORNAMENTO 4:

Come accennato in uno dei commenti sotto la mia risposta, il stringifymetodo JSON non funzionerà immediatamente per tutti i tipi di oggetti. Per ulteriori informazioni sui tipi supportati, fare riferimento alla sezione Descrizione nella documentazione MDN diJSON.stringify .

Nella descrizione si dice anche che:

Se il valore ha un metodo toJSON (), è responsabile di definire quali dati verranno serializzati.

Ciò significa che è possibile fornire il proprio toJSONmetodo di serializzazione con logica per serializzare gli oggetti personalizzati. In questo modo è possibile creare rapidamente e facilmente il supporto della serializzazione per alberi di oggetti più complessi.


1
Come accennato nella risposta di @TomasPrado, assicurati di non aver bisogno del supporto per IE11.
Wilt

4
Questo non funziona con gli elementi del modulo a selezione multipla poiché condividono la stessa chiave, si finisce per sovrascrivere i valori restituendo solo l'ultimo elemento selezionato
Sean

1
@Sean ho dato una risposta che funziona con più valori per <SELECT MULTIPLE>e <INPUT type="checkbox">con lo stesso nome, convertendo il valore in un array.
alcuni il

formData non è una serie. come puoi iterare? risposta accettata ma qualcosa mancato.
Nuri YILMAZ

4
A meno che non sia necessario il multi-selects ecc., La risposta JSON.stringify(Object.fromEntries(formData));è molto più carina
Tom Stickel

108

Nel 2019, questo tipo di attività è diventato semplicissimo.

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

Object.fromEntries: Supportato in Chrome 73+, Firefox 63+, Safari 12.1


2
Sono venuto qui per postare questo, adoro guardare questo thread e vedere come si sono evolute le risposte nel corso degli anni
Marcin

13
Ciò non sembra funzionare correttamente sui moduli che hanno più campi con lo stesso nome.
JukkaP

3
È il 2020 e questo non gestisce più valori selezionati di <select multiple>o <input type="checkbox"> 😞
alcuni il

4
Meglio usare formData.entries:JSON.stringify(Object.fromEntries(formData.entries()));
Kohver

22

Ecco un modo per farlo in uno stile più funzionale, senza l'uso di una libreria.

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

Esempio:

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
La migliore risposta qui. Grazie :)
Chunky Chunk

4
Mi piace molto questa risposta ma ancora non gestisco più elementi. Ho pubblicato una nuova risposta basata su questa per gestire quei casi.
CarlosH.

9

Se hai più voci con lo stesso nome, ad esempio se ne usi <SELECT multiple>o ne hai più <INPUT type="checkbox">con lo stesso nome, devi occupartene e creare un array del valore. Altrimenti ottieni solo l'ultimo valore selezionato.

Ecco la moderna variante 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 );
}

Codice leggermente più vecchio (ma ancora non supportato da IE11, poiché non supporta ForEacho entriesattivaFormData )

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

È possibile ottenere ciò utilizzando FormData () oggetto . Questo oggetto FormData verrà popolato con le chiavi / valori correnti del modulo utilizzando la proprietà name di ogni elemento per le chiavi e il valore inviato per i valori. Codificherà anche il contenuto di input del file.

Esempio:

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

});

Questo non produce json
Liam

1
@Liam Hai provato con gli elementi del modulo? E fammi sapere perché non produce oggetto JSON?
GiriB

1
Non esiste un oggetto json. Json è una notazione di stringa
Liam

1
@Liam Dopo aver creato l'oggetto, possono utilizzare JSON.stringify (risultato). E ho modificato la mia risposta. Per favore controllalo. E ritirare il voto negativo.
GiriB

1
Puoi anche rendere la frase for of più espressiva se stai usando ES6: for (const [key, value] of formData.entries())
Teddy Zetterlund

7

Funzione facile da usare

Ho creato una funzione per questo

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

    return ConvertedJSON
}

Utilizzo di esempio

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

In questo codice ho creato una variabile JSON vuota usando il forloop che ho usato keys da formData Object a JSON Keys in ogni Itration.

Trovi questo codice nella mia libreria JS su GitHub Suggeriscimi se ha bisogno di miglioramenti Ho inserito il codice qui https://github.com/alijamal14/Utilities/blob/master/Utilities.js


1
@zuluk ha spiegato grazie
Ali Jamal

Questo non gestisce più valori selezionati di <select multiple>o <input type="checkbox">.
alcuni il

5

Questo post è già vecchio di un anno ... ma mi piace davvero molto la risposta di ES6 @dzuc. Tuttavia è incompleto perché non è stato possibile gestire più selezioni o caselle di controllo. Questo è già stato indicato e sono state offerte soluzioni di codice. Li trovo pesanti e non ottimizzati. Quindi ho scritto 2 versioni basate su @dzuc per gestire questi casi:

  • Per i moduli in stile ASP in cui il nome di più elementi potrebbe essere semplicemente ripetuto.
let r=Array.from(fd).reduce(
  (o , [k,v]) => (
     (!o[k])
     ? {...o , [k] : v}
     : {...o , [k] : [...o[k] , v]}
   )
   ,{}
);
let obj=JSON.stringify(r);

Versione Hotshot a una riga:

Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
  • Per i moduli in stile PHP in cui i nomi di più elementi devono avere un []suffisso.
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);

Versione Hotshot a una riga:

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}),{});
  • Estensione del modulo PHP che supporta array multilivello.

Dall'ultima volta che ho scritto il secondo caso precedente, al lavoro si è verificato un caso in cui il modulo PHP ha caselle di controllo su più livelli. Ho scritto un nuovo caso per supportare il caso precedente e questo. Ho creato uno snippet per mostrare meglio questo caso, il risultato mostrato sulla console per questa demo, modificalo secondo le tue necessità. Ho cercato di ottimizzarlo il meglio che potevo senza compromettere le prestazioni, tuttavia, ha compromesso la leggibilità umana. Si avvantaggia del fatto che gli array sono oggetti e le variabili che puntano agli array vengono mantenute come riferimento. Nessun hotshot per questo, sii mio ospite.

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}), {});versione hotshot es2018
nackjicholson

1
@nackjicholson Yeap, hai ragione, omettere .entries () e l'elemento [k, v] semplifica il codice. Riscriverò il codice per includere questi miglioramenti. Tuttavia, il tuo verrà comunque sovrascritto sui valori ripetuti.
CarlosH.

3
Anche se apprezzo lo sforzo, un codice come questo è assurdo. Nessuno vuole guardare le lettere per variabili e oggetti, questo non è il 1985.
Tom Stickel

4

Il metodo FormData .entriese l' for ofespressione non sono supportati in IE11 e Safari.

Ecco una versione più semplice per supportare Safari, Chrome, Firefox e Edge

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

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

    return convertedJSON;
}

Attenzione: questa risposta non funziona in IE11.
FormData non dispone di un forEachmetodo in IE11.
Sto ancora cercando una soluzione finale per supportare tutti i principali browser.


questo è perfetto! dobbiamo supportare i browser più vecchi e l'uso dell'iteratore non è molto intuitivo.
Peter Hawkins

3

Se stai usando lodash, puoi farlo in modo conciso fromPairs

import {fromPairs} from 'lodash';

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

3

Se hai bisogno di supporto per serializzare i campi nidificati, in modo simile a come PHP gestisce i campi modulo, puoi usare la seguente funzione

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
È necessario aggiornare il valore predefinito di "dati" dall'array "[]" all'oggetto "{}" in "aggiornamento della funzione (dati, chiavi, valore) {" rimuove il problema dell'array vuoto.
Chintan Mathukiya

Suggerisco anche di aggiungere un filtro array prima di ridurre per rimuovere i campi vuoti dall'oggetto finale
Ednilson Maia

@ChintanMathukiya Maia Puoi condividere l'input di esempio per il quale hai un output inaspettato?
Joyce Babu

@Ednilson Potete condividere i dati di esempio per i quali state vedendo un output di array vuoto?
Joyce Babu

2

Puoi provare questo

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

Anche se la risposta di @dzuc è già molto buona, potresti usare la destrutturazione dell'array (disponibile nei browser moderni o con Babel) per renderlo ancora un po 'più elegante:

// 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

One-liner abusivo!

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

Oggi ho appreso che Firefox supporta la diffusione degli oggetti e la destrutturazione degli array!


1

Se i seguenti elementi soddisfano le tue esigenze, sei fortunato:

  1. Vuoi convertire un array di array come [['key','value1'], ['key2','value2'](come quello che ti dà FormData) in un oggetto chiave-> valore come {key1: 'value1', key2: 'value2'}e convertirlo in una stringa JSON.
  2. Stai prendendo di mira browser / dispositivi con l'ultimo interprete ES6 o stai compilando qualcosa come babel.
  3. Vuoi il modo più piccolo per ottenere questo risultato.

Ecco il codice che ti servirà:

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

Spero che questo aiuti qualcuno.


1

Finora non ho visto menzioni del metodo FormData.getAll .

Oltre a restituire tutti i valori associati a una determinata chiave dall'interno di un oggetto FormData, diventa davvero semplice utilizzare il metodo Object.fromEntries come specificato da altri qui.

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

Snippet in azione

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

Ha funzionato per me

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

Non gestirà più valori selezionati di <select multiple>o<input type="checkbox">
alcuni

0

Nel mio caso i dati erano dati, la base antincendio si aspettava un oggetto ma i dati contengono l'oggetto e tutte le altre cose, quindi ho provato data.value ha funzionato !!!


0

Arrivo tardi qui. Tuttavia, ho creato un metodo semplice che controlla il tipo di input = "casella di controllo"

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

Spero che questo aiuti qualcun altro.


-1

Puoi fare questo lavoro facilmente senza usare nulla di speciale. Un codice come quello qui sotto sarà sufficiente.

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;
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.