¿Cómo convertir un objeto FormData HTML5 a JSON? Sin Jquery y manejando propiedades anidadas en FormData como un objeto.
¿Cómo convertir un objeto FormData HTML5 a JSON? Sin Jquery y manejando propiedades anidadas en FormData como un objeto.
Respuestas:
También puede utilizar forEach
en elFormData
objeto directamente:
var object = {};
formData.forEach(function(value, key){
object[key] = value;
});
var json = JSON.stringify(object);
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);
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.
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 FormData
objeto 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 :
Como se menciona en uno de los comentarios a continuación, mi respuesta, el stringify
mé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 toJSON
mé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.
<SELECT MULTIPLE>
y <INPUT type="checkbox">
con el mismo nombre, convirtiendo el valor en una matriz.
JSON.stringify(Object.fromEntries(formData));
es mucho mejor
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
<select multiple>
o <input type="checkbox">
😞
JSON.stringify(Object.fromEntries(formData.entries()));
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>
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 ForEach
o entries
no 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 );
}
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);
});
for (const [key, value] of formData.entries())
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 for
bucle 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
<select multiple>
o <input type="checkbox">
.
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:
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]}),{});
[]
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}),{});
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>
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});
Hotshot versión es2018
El método FormData .entries
y la for of
expresió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 forEach
método en IE11.
Todavía estoy buscando una solución final que sea compatible con los principales navegadores.
Si está usando lodash, puede hacerlo de forma concisa con fromPairs
import {fromPairs} from 'lodash';
const object = fromPairs(Array.from(formData.entries()));
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>
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);
}
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,
}), {})
¡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!
Si los siguientes elementos satisfacen sus necesidades, está de suerte:
[['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.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.
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>
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);
<select multiple>
o<input type="checkbox">
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ó!
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.
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;
};
JSON.stringify()
Ayuda? ¿Quizás intentas arreglar algo que se puede hacer de otra manera?