Comment convertir FormData (objet HTML5) en JSON

104

Comment convertir un objet HTML5 FormData en JSON? Sans Jquery et gestion des propriétés imbriquées dans FormData comme un objet.

Léonard de Vinci
la source
2
Qu'essayez-vous de faire? Ça JSON.stringify()aide? Peut-être que vous essayez de réparer quelque chose qui pourrait être fait d'une autre manière?
Justinas
Double possible de Convertir l'objet JS en chaîne JSON
Liam
3
N'est pas dupliqué car je ne veux pas convertir un objet javascript en json, ni utiliser Jquery.serialize ()
Leonardo Villela
Vérifiez ceci: stackoverflow.com/a/39248551/6293856
Bhavik Hirani

Réponses:

145

Vous pouvez également utiliser directement forEachsur l' FormDataobjet:

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

METTRE À JOUR:

Et pour ceux qui préfèrent la même solution avec les fonctions fléchées ES6 :

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

MISE À JOUR 2:

Et pour ceux qui souhaitent prendre en charge les listes de sélection multiples ou d'autres éléments de formulaire avec plusieurs valeurs (car il y a tellement de commentaires sous la réponse concernant ce problème, j'ajouterai une solution possible) :

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

Voici un Fiddle démontrant l'utilisation de cette méthode avec une simple liste de sélection multiple.

MISE À JOUR 3:

En guise de note supplémentaire pour ceux qui se retrouvent ici, si le but de la conversion des données du formulaire en json est de l'envoyer via une requête HTTP XML à un serveur, vous pouvez envoyer l' FormDataobjet directement sans le convertir. Aussi simple que ceci:

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

Voir aussi Utilisation d'objets FormData sur MDN pour référence :

MISE À JOUR 4:

Comme mentionné dans l'un des commentaires ci-dessous ma réponse, la stringifyméthode JSON ne fonctionnera pas hors de la boîte pour tous les types d'objets. Pour plus d'informations sur les types pris en charge, je voudrais consulter la section Description de la documentation MDN deJSON.stringify .

Dans la description, il est également mentionné que:

Si la valeur a une méthode toJSON (), il est responsable de définir les données qui seront sérialisées.

Cela signifie que vous pouvez fournir votre propre toJSONméthode de sérialisation avec une logique de sérialisation de vos objets personnalisés. Ainsi, vous pouvez rapidement et facilement créer une prise en charge de la sérialisation pour des arborescences d'objets plus complexes.

Se flétrir
la source
1
Comme mentionné dans la réponse de @TomasPrado, assurez-vous de ne pas avoir besoin de support pour IE11.
Wilt
4
Cela ne fonctionne pas avec les éléments de formulaire à sélection multiple car ils partagent la même clé que vous finissez par écraser les valeurs en retournant uniquement le dernier élément sélectionné
Sean
1
@Sean J'ai donné une réponse qui fonctionne avec plusieurs valeurs pour <SELECT MULTIPLE>et <INPUT type="checkbox">avec le même nom, en convertissant la valeur en tableau.
du
formData n'est pas une série. comment pouvez-vous itérer? réponse acceptée mais quelque chose a manqué.
Nuri YILMAZ
4
À moins d'avoir besoin de sélections multiples, etc., la réponse JSON.stringify(Object.fromEntries(formData));est tellement plus agréable
Tom Stickel
108

En 2019, ce genre de tâche est devenu super facile.

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

Object.fromEntries: Pris en charge dans Chrome 73+, Firefox 63+, Safari 12.1

hakatashi
la source
2
Je suis venu ici pour poster ceci, j'adore regarder ce fil et voir comment les réponses ont évolué au fil des ans
Marcin
13
Cela ne semble pas fonctionner correctement sur les formulaires qui ont plusieurs champs avec le même nom.
JukkaP
3
Nous sommes en 2020 et cela ne gère pas plusieurs valeurs sélectionnées de <select multiple>ou <input type="checkbox">😞
certains
4
Mieux vaut utiliser formData.entries:JSON.stringify(Object.fromEntries(formData.entries()));
Kohver
22

Voici une façon de le faire dans un style plus fonctionnel, sans utiliser de bibliothèque.

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

Exemple:

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
la source
1
Meilleure réponse ici. Merci :)
Chunky Chunk
4
J'aime vraiment cette réponse mais je ne gère toujours pas plusieurs éléments. J'ai publié une nouvelle réponse basée sur celle-ci pour gérer ces cas.
CarlosH.
9

Si vous avez plusieurs entrées avec le même nom, par exemple si vous utilisez <SELECT multiple>ou en avez plusieurs <INPUT type="checkbox">avec le même nom, vous devez vous en occuper et créer un tableau de la valeur. Sinon, vous n'obtiendrez que la dernière valeur sélectionnée.

Voici la variante ES6 moderne:

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

Code peu plus (mais toujours pas pris en charge par IE11, car il ne supporte pas ForEachou entriessur 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 );
  }
certains
la source
7

Vous pouvez y parvenir en utilisant l' objet FormData () . Cet objet FormData sera rempli avec les clés / valeurs actuelles du formulaire en utilisant la propriété name de chaque élément pour les clés et leur valeur soumise pour les valeurs. Il encodera également le contenu d'entrée du fichier.

Exemple:

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
la source
Cela ne produit pas json
Liam
1
@Liam Avez-vous essayé cela avec des éléments de formulaire? Et laissez-moi savoir pourquoi il ne produit pas d'objet JSON?
GiriB
1
Il n'existe pas d'objet json. Json est une notation de chaîne
Liam
1
@Liam Une fois l'objet créé, ils peuvent utiliser JSON.stringify (résultat). Et j'ai édité ma réponse. S'il te plaît vérifie le. Et retirez le vote négatif.
GiriB
1
Vous pouvez également rendre la déclaration for of plus expressive si vous utilisez ES6: for (const [key, value] of formData.entries())
Teddy Zetterlund
7

Fonction facile à utiliser

J'ai créé une fonction pour cela

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

    return ConvertedJSON
}

Exemple d'utilisation

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

Dans ce code, j'ai créé une variable JSON vide à l'aide d'une boucle.J'ai forutilisé des keys de l'objet formData aux clés JSON dans chaque Itration.

Vous trouvez ce code dans ma bibliothèque JS sur GitHub, suggérez-moi s'il a besoin d'amélioration, j'ai placé le code ici https://github.com/alijamal14/Utilities/blob/master/Utilities.js

Ali Jamal
la source
1
@zuluk a expliqué merci
Ali Jamal
Cela ne gère pas plusieurs valeurs sélectionnées de <select multiple>ou <input type="checkbox">.
du
5

Ce message a déjà un an ... mais j'aime vraiment beaucoup la réponse ES6 @dzuc. Cependant, il est incomplet car il n'a pas été en mesure de gérer plusieurs sélections ou cases à cocher. Cela a déjà pointé et des solutions de code ont été proposées. Je les trouve lourds et non optimisés. J'ai donc écrit 2 versions basées sur @dzuc pour gérer ces cas:

  • Pour les formulaires de style ASP où le nom de plusieurs éléments peut simplement être répété.
let r=Array.from(fd).reduce(
  (o , [k,v]) => (
     (!o[k])
     ? {...o , [k] : v}
     : {...o , [k] : [...o[k] , v]}
   )
   ,{}
);
let obj=JSON.stringify(r);

Version Hotshot une ligne:

Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
  • Pour les formulaires de style PHP où les noms d'éléments multiples doivent avoir un []suffixe.
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);

Version Hotshot une ligne:

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}),{});
  • Extension du formulaire PHP prenant en charge les tableaux multi-niveaux.

Depuis la dernière fois que j'ai écrit le deuxième cas précédent, au travail, il est arrivé que le formulaire PHP ait des cases à cocher sur plusieurs niveaux. J'ai écrit un nouveau cas pour soutenir le cas précédent et celui-ci. J'ai créé un extrait pour mieux mettre en valeur ce cas, le résultat affiché sur la console pour cette démo, modifiez-le selon vos besoins. J'ai essayé de l'optimiser du mieux que je pouvais sans compromettre les performances, mais cela compromettait une certaine lisibilité humaine. Il tire parti du fait que les tableaux sont des objets et que les variables pointant vers des tableaux sont conservées comme référence. Pas de hotshot pour celui-ci, soyez mon invité.

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.
la source
2
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});hotshot version es2018
nackjicholson
1
@nackjicholson Oui, vous avez raison, omettre .entries () et l'élément [k, v] simplifient le code. Je vais réécrire le code pour inclure ces améliorations. Cependant, le vôtre écrasera toujours les valeurs répétées.
CarlosH.
3
Bien que j'apprécie l'effort, un code comme celui-ci est absurde. Personne ne veut regarder les lettres pour les variables et les objets, ce n'est pas 1985.
Tom Stickel
4

La méthode FormData .entrieset l' for ofexpression ne sont pas prises en charge dans IE11 et Safari.

Voici une version plus simple pour prendre en charge Safari, Chrome, Firefox et Edge

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

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

    return convertedJSON;
}

Attention: cette réponse ne fonctionne pas dans IE11.
FormData n'a pas de forEachméthode dans IE11.
Je suis toujours à la recherche d'une solution finale pour prendre en charge tous les principaux navigateurs.

Tomas Prado
la source
c'est parfait! nous devons prendre en charge les navigateurs plus anciens et l'utilisation de l'itérateur n'est pas très intuitive.
Peter Hawkins
3

Si vous utilisez lodash, cela peut être fait de manière concise avec fromPairs

import {fromPairs} from 'lodash';

const object = fromPairs(Array.from(formData.entries()));
Erik van Velzen
la source
3

Si vous avez besoin de support pour la sérialisation des champs imbriqués, de la même manière que PHP gère les champs de formulaire, vous pouvez utiliser la fonction suivante

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
la source
1
Besoin de mettre à jour la valeur par défaut de "data" du tableau "[]" à l'objet "{}" dans "function update (données, clés, valeur) {" il supprime le problème du tableau vide.
Chintan Mathukiya le
Je suggère également d'ajouter un filtre de tableau avant de réduire pour supprimer les champs vides de l'objet final
Ednilson Maia
@ChintanMathukiya Maia Pouvez-vous partager l'échantillon d'entrée pour lequel vous avez une sortie inattendue?
Joyce Babu le
@Ednilson Pouvez-vous partager les exemples de données pour lesquels vous voyez une sortie de tableau vide?
Joyce Babu le
2

Vous pouvez essayer ceci

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
la source
2

Même si la réponse de @dzuc est déjà très bonne, vous pouvez utiliser la déstructuration des tableaux (disponible dans les navigateurs modernes ou avec Babel) pour le rendre encore un peu plus élégant:

// 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
la source
2

Une doublure abusive!

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

Aujourd'hui, j'ai appris que Firefox prend en charge la diffusion d'objets et la déstructuration des tableaux!

Nackjicholson
la source
1

Si les articles suivants répondent à vos besoins, vous avez de la chance:

  1. Vous voulez convertir un tableau de tableaux comme [['key','value1'], ['key2','value2'](comme ce que FormData vous donne) en un objet clé-> valeur comme {key1: 'value1', key2: 'value2'}et le convertir en une chaîne JSON.
  2. Vous ciblez des navigateurs / appareils avec le dernier interpréteur ES6 ou compilez avec quelque chose comme babel.
  3. Vous voulez le plus petit moyen d'y parvenir.

Voici le code dont vous aurez besoin:

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

J'espère que cela aide quelqu'un.

KyleFarris
la source
1

Je n'ai vu aucune mention de la méthode FormData.getAll jusqu'à présent.

En plus de renvoyer toutes les valeurs associées à une clé donnée à partir d'un objet FormData, cela devient vraiment simple en utilisant la méthode Object.fromEntries comme spécifié par d'autres ici.

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

Extrait en action

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
la source
0

A travaillé pour moi

                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
la source
Il ne gérera pas plusieurs valeurs sélectionnées de <select multiple>ou<input type="checkbox">
certaines du
0

Dans mon cas, les données étaient des données, la base de feu attendait un objet mais les données contiennent un objet ainsi que tous les autres éléments, alors j'ai essayé data.value, cela a fonctionné !!!

Rahul solanki
la source
0

J'arrive tard ici. Cependant, j'ai fait une méthode simple qui vérifie le type d'entrée = "case à cocher"

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

J'espère que cela aide quelqu'un d'autre.

acido
la source
-1

Vous pouvez faire ce travail facilement sans utiliser quoi que ce soit de spécial. Un code comme celui ci-dessous suffira.

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
la source