Cómo convertir FormData (objeto HTML5) a JSON

104

¿Cómo convertir un objeto FormData HTML5 a JSON? Sin Jquery y manejando propiedades anidadas en FormData como un objeto.

Leonardo Villela
fuente
2
¿Que estás tratando de hacer? ¿ JSON.stringify()Ayuda? ¿Quizás intentas arreglar algo que se puede hacer de otra manera?
Justinas
Posible duplicado de Convertir objeto JS en cadena JSON
Liam
3
No está duplicado ya que no quiero convertir un objeto javascript en un json, ni quiero usar Jquery.serialize ()
Leonardo Villela
verifique esto: stackoverflow.com/a/39248551/6293856
Bhavik Hirani

Respuestas:

145

También puede utilizar forEachen elFormData objeto directamente:

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

ACTUALIZAR:

Y para aquellos que prefieren la misma solución con las funciones de flecha ES6 :

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

ACTUALIZACIÓN 2:

Y para aquellos que desean soporte para listas de selección múltiple u otros elementos de formulario con múltiples valores (dado que hay tantos comentarios debajo de la respuesta con respecto a este problema, agregaré una posible solución) :

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

Aquí un violín demuestra el uso de este método con una lista de selección múltiple simple.

ACTUALIZACIÓN 3:

Como nota al margen para aquellos que terminan aquí, en caso de que el propósito de convertir los datos del formulario a json sea enviarlos a través de una solicitud HTTP XML a un servidor, puede enviar el FormDataobjeto directamente sin convertirlo. Tan simple como esto:

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

Consulte también Uso de objetos FormData en MDN como referencia :

ACTUALIZACIÓN 4:

Como se menciona en uno de los comentarios a continuación, mi respuesta, el stringifymétodo JSON no funcionará de inmediato para todos los tipos de objetos. Para obtener más información sobre qué tipos son compatibles, me gustaría consultar la sección Descripción en la documentación de MDN deJSON.stringify .

En la descripción también se menciona que:

Si el valor tiene un método toJSON (), es responsable de definir qué datos se serializarán.

Esto significa que puede proporcionar su propio toJSONmétodo de serialización con lógica para serializar sus objetos personalizados. De esta manera, puede crear soporte de serialización rápida y fácilmente para árboles de objetos más complejos.

Marchitar
fuente
1
Como se menciona en la respuesta de @TomasPrado, asegúrese de no necesitar soporte para IE11.
March
4
Esto no funciona con elementos de formulario de selección múltiple, ya que comparten la misma clave, termina sobrescribiendo los valores y devuelve solo el último elemento seleccionado
Sean
1
@Sean Di una respuesta que funciona con múltiples valores para <SELECT MULTIPLE>y <INPUT type="checkbox">con el mismo nombre, convirtiendo el valor en una matriz.
algo del
formData no es una serie. ¿cómo se puede iterar? respuesta aceptada, pero algo se perdió.
Nuri YILMAZ
4
A menos que se necesiten múltiples selecciones, etc., la respuesta JSON.stringify(Object.fromEntries(formData));es mucho mejor
Tom Stickel
108

En 2019, este tipo de tarea se volvió súper fácil.

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

Object.fromEntries: Compatible con Chrome 73+, Firefox 63+, Safari 12.1

hakatashi
fuente
2
Vine aquí para publicar esto, me encanta mirar este hilo y ver cómo han evolucionado las respuestas durante los años
Marcin
13
Esto no parece funcionar correctamente en formularios que tienen varios campos con el mismo nombre.
JukkaP
3
Es 2020 y esto no maneja múltiples valores seleccionados de <select multiple>o <input type="checkbox"> 😞
algunos
4
Es mejor usar formData.entries:JSON.stringify(Object.fromEntries(formData.entries()));
Kohver
22

He aquí una forma de hacerlo con un estilo más funcional, sin el uso de una biblioteca.

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

Ejemplo:

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
fuente
1
La mejor respuesta aquí. Gracias :)
Chunky Chunk
4
Realmente me gusta esta respuesta, pero aún no manejo varios elementos. Publiqué una nueva respuesta basada en esta para manejar esos casos.
CarlosH.
9

Si tiene varias entradas con el mismo nombre, por ejemplo, si utiliza <SELECT multiple>o tiene varias<INPUT type="checkbox"> con el mismo nombre, debe ocuparse de eso y hacer una matriz del valor. De lo contrario, solo obtendrá el último valor seleccionado.

Aquí está la variante moderna 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 );
}

Código un poco más antiguo (pero aún no es compatible con IE11, ya que no es compatible ForEacho entriesno 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 );
  }
algunos
fuente
7

Puede lograr esto utilizando FormData () objeto . Este objeto FormData se completará con las claves / valores actuales del formulario utilizando la propiedad de nombre de cada elemento para las claves y su valor enviado para los valores. También codificará el contenido de entrada del archivo.

Ejemplo:

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
fuente
Esto no produce json
Liam
1
@Liam ¿Has probado esto con elementos de formulario? Y déjame saber por qué no produce un objeto JSON.
GiriB
1
No existe un objeto json. Json es una notación de cadena
Liam
1
@Liam Después de crear el objeto, pueden usar JSON.stringify (resultado). Y he editado mi respuesta. Compruébelo por favor. Y retirar el voto negativo.
GiriB
1
También puede hacer que la declaración de for of sea más expresiva si está usando ES6: for (const [key, value] of formData.entries())
Teddy Zetterlund
7

Función fácil de usar

He creado una función para esto

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

    return ConvertedJSON
}

Ejemplo de uso

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

En este código, he creado una variable JSON vacía usando el forbucle que he usadokey s de formData Object a JSON Keys en cada iteración.

Encuentra este código en mi biblioteca JS en GitHub, sugiéreme si necesita una mejora. He colocado el código aquí https://github.com/alijamal14/Utilities/blob/master/Utilities.js

Ali Jamal
fuente
1
@zuluk Explicado Gracias
Ali Jamal
Esto no maneja múltiples valores seleccionados de <select multiple>o <input type="checkbox">.
un
5

Esta publicación ya tiene un año ... pero, realmente, me gusta mucho la respuesta de ES6 @dzuc. Sin embargo, está incompleto al no poder manejar múltiples selecciones o casillas de verificación. Esto ya se ha apuntado y se han ofrecido soluciones de código. Los encuentro pesados ​​y no optimizados. Entonces escribí 2 versiones basadas en @dzuc para manejar estos casos:

  • Para formularios de estilo ASP donde el nombre de varios elementos podría simplemente repetirse.
let r=Array.from(fd).reduce(
  (o , [k,v]) => (
     (!o[k])
     ? {...o , [k] : v}
     : {...o , [k] : [...o[k] , v]}
   )
   ,{}
);
let obj=JSON.stringify(r);

Versión Hotshot de una línea:

Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
  • Para formularios de estilo PHP donde los nombres de varios elementos deben tener un []sufijo.
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);

Versión Hotshot de una línea:

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}),{});
  • Extensión del formulario PHP que admite matrices de varios niveles.

Desde la última vez que escribí el segundo caso anterior, en el trabajo surgió un caso en el que el formulario PHP tiene casillas de verificación en varios niveles. Escribí un nuevo caso para respaldar el caso anterior y este. Creé un fragmento para mostrar mejor este caso, el resultado se muestra en la consola para esta demostración, modifíquelo según sus necesidades. Traté de optimizarlo lo mejor que pude sin comprometer el rendimiento, sin embargo, comprometió la legibilidad humana. Se aprovecha que las matrices son objetos y las variables que apuntan a matrices se mantienen como referencia. No hay pez gordo para este, sea mi invitado.

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.
fuente
2
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});Hotshot versión es2018
nackjicholson
1
@nackjicholson Sí, tienes razón, omitiendo .entries () y el elemento [k, v] simplifica el código. Volveré a escribir el código para incluir estas mejoras. Sin embargo, el tuyo seguirá sobrescribiendo los valores repetidos.
CarlosH.
3
Si bien aprecio el esfuerzo, un código como este es absurdo. Nadie quiere buscar variables y objetos en las letras, esto no es 1985.
Tom Stickel
4

El método FormData .entriesy la for ofexpresión no son compatibles con IE11 y Safari.

Aquí hay una versión más simple para admitir Safari, Chrome, Firefox y Edge

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

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

    return convertedJSON;
}

Advertencia: esta respuesta no funciona en IE11.
FormData no tiene un forEachmétodo en IE11.
Todavía estoy buscando una solución final que sea compatible con los principales navegadores.

Tomás Prado
fuente
¡esto es perfecto! tenemos que admitir navegadores más antiguos y el uso del iterador no es muy intuitivo.
Peter Hawkins
3

Si está usando lodash, puede hacerlo de forma concisa con fromPairs

import {fromPairs} from 'lodash';

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

Si necesita soporte para serializar campos anidados, similar a cómo PHP maneja los campos de formulario, puede usar la siguiente función

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
fuente
1
Necesita actualizar el valor predeterminado de "datos" de la matriz "[]" al objeto "{}" en "actualización de función (datos, claves, valor) {" elimina el problema de la matriz en blanco.
Chintan Mathukiya
También sugiero agregar un filtro de matriz antes de reducir para eliminar los campos vacíos del objeto final
Ednilson Maia
@ChintanMathukiya Maia ¿Puede compartir la entrada de muestra para la que tiene una salida inesperada?
Joyce Babu
@Ednilson ¿Puede compartir los datos de muestra para los que está viendo una salida de matriz vacía?
Joyce Babu
2

Puedes probar esto

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
fuente
2

Aunque la respuesta de @dzuc ya es muy buena, puede usar la desestructuración de matrices (disponible en navegadores modernos o con Babel) para hacerlo aún un poco más 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,
  }), {})
Jeremias Erbs
fuente
2

¡Una sola línea abusiva!

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

¡Hoy aprendí que Firefox tiene soporte para propagación de objetos y desestructuración de matrices!

Nackjicholson
fuente
1

Si los siguientes elementos satisfacen sus necesidades, está de suerte:

  1. Desea convertir una matriz de matrices como [['key','value1'], ['key2','value2'](como lo que FormData le da) en un objeto clave-> valor como{key1: 'value1', key2: 'value2'} y convertirlo en una cadena JSON.
  2. Está apuntando a navegadores / dispositivos con el último intérprete de ES6 o está compilando con algo como babel.
  3. Quieres la forma más pequeña de lograr esto.

Aquí está el código que necesitará:

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

Espero que esto ayude a alguien.

KyleFarris
fuente
1

No he visto menciones de FormData.getAllHasta ahora método .

Además de devolver todos los valores asociados con una clave dada desde dentro de un objeto FormData, se vuelve realmente simple usar el método Object.fromEntries como lo especifican otros aquí.

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

Fragmento en acción

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
fuente
0

Trabajó para mi

                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
fuente
No manejará múltiples valores seleccionados de <select multiple>o<input type="checkbox">
algunos del
0

En mi caso, los datos del formulario eran datos, la base de fuego esperaba un objeto, pero los datos contienen el objeto y todas las demás cosas, así que probé data.value, ¡funcionó!

Rahul solanki
fuente
0

Llego tarde aquí. Sin embargo, hice un método simple que busca el tipo de entrada = "casilla de verificación"

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

Espero que esto ayude a alguien más.

acido
fuente
-1

Puede hacer este trabajo fácilmente sin usar nada especial. Un código como el siguiente será suficiente.

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
fuente