Como converter o objeto FormData HTML5 para JSON? Sem Jquery e manipulando propriedades aninhadas em FormData como um objeto.
Como converter o objeto FormData HTML5 para JSON? Sem Jquery e manipulando propriedades aninhadas em FormData como um objeto.
Respostas:
Você também pode usar forEach
no FormData
objeto diretamente:
var object = {};
formData.forEach(function(value, key){
object[key] = value;
});
var json = JSON.stringify(object);
E para quem prefere a mesma solução com as funções de seta ES6 :
var object = {};
formData.forEach((value, key) => {object[key] = value});
var json = JSON.stringify(object);
E para aqueles que desejam suporte para listas de seleção múltipla ou outros elementos de formulário com valores múltiplos (já que existem tantos comentários abaixo da resposta a respeito desse problema, adicionarei uma possível solução) :
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);
Aqui está um Fiddle demonstrando o uso desse método com uma lista de seleção múltipla simples.
Como uma nota lateral para quem vai parar aqui, caso o objetivo de converter os dados do formulário para json seja enviá-los por meio de uma solicitação HTTP XML para um servidor, você pode enviar o FormData
objeto diretamente sem convertê-lo. Simples assim:
var request = new XMLHttpRequest();
request.open("POST", "http://example.com/submitform.php");
request.send(formData);
Consulte também Usando objetos FormData no MDN para referência :
Conforme mencionado em um dos comentários abaixo, minha resposta o stringify
método JSON não funcionará fora da caixa para todos os tipos de objetos. Para obter mais informações sobre quais tipos são suportados, gostaria de consultar a seção Descrição na documentação do MDN deJSON.stringify
.
Na descrição também é mencionado que:
Se o valor possuir um método toJSON (), ele é responsável por definir quais dados serão serializados.
Isso significa que você pode fornecer seu próprio toJSON
método de serialização com lógica para serializar seus objetos personalizados. Assim, você pode construir suporte de serialização de forma rápida e fácil para árvores de objetos mais complexas.
<SELECT MULTIPLE>
e <INPUT type="checkbox">
com o mesmo nome, convertendo o valor em um array.
JSON.stringify(Object.fromEntries(formData));
é muito mais agradável
Em 2019, esse tipo de tarefa ficou super fácil.
JSON.stringify(Object.fromEntries(formData));
Object.fromEntries
: Compatível com Chrome 73+, Firefox 63+, Safari 12.1
<select multiple>
ou <input type="checkbox">
😞
JSON.stringify(Object.fromEntries(formData.entries()));
Esta é uma maneira de fazer isso em um estilo mais funcional, sem o uso de uma biblioteca.
Array.from(formData.entries()).reduce((memo, pair) => ({
...memo,
[pair[0]]: pair[1],
}), {});
Exemplo:
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>
Se você tiver várias entradas com o mesmo nome, por exemplo, se você usar <SELECT multiple>
ou tiver várias <INPUT type="checkbox">
com o mesmo nome, você precisa cuidar disso e fazer uma matriz do valor. Caso contrário, você obtém apenas o último valor selecionado.
Aqui está a variante ES6 moderna:
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 ligeiramente mais velho (mas ainda não é suportado pelo IE11, uma vez que não suporta ForEach
ou entries
on 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 );
}
Você pode fazer isso usando o objeto FormData () . Este objeto FormData será preenchido com as chaves / valores atuais do formulário usando a propriedade name de cada elemento para as chaves e seu valor enviado para os valores. Ele também codificará o conteúdo de entrada do arquivo.
Exemplo:
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())
Função fácil de usar
Eu criei uma função para isso
function FormDataToJSON(FormElement){
var formData = new FormData(FormElement);
var ConvertedJSON= {};
for (const [key, value] of formData.entries())
{
ConvertedJSON[key] = value;
}
return ConvertedJSON
}
Exemplo de uso
var ReceivedJSON = FormDataToJSON(document.getElementById('FormId');)
Neste código, criei uma variável JSON vazia usando o for
loop. Usei key
s do objeto formData para Chaves JSON em cada Itração.
Você encontra este código na minha biblioteca JS no GitHub Sugira-me se precisar de melhorias Coloquei o código aqui https://github.com/alijamal14/Utilities/blob/master/Utilities.js
<select multiple>
ou <input type="checkbox">
.
Este post já tem um ano ... mas, realmente, gostei muito da resposta do ES6 @dzuc. No entanto, está incompleto por não ser capaz de lidar com várias seleções ou caixas de seleção. Isso já apontou e soluções de código foram oferecidas. Acho que são pesados e não otimizados. Então, escrevi 2 versões baseadas em @dzuc para lidar com esses 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);
Versão Hotshot de uma linha:
Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
[]
sufixo.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);
Versão Hotshot de uma linha:
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 a última vez que escrevi o segundo caso anterior, no trabalho surgiu um caso em que o formulário do PHP tinha caixas de seleção em vários níveis. Eu escrevi um novo caso para apoiar o caso anterior e este. Criei um trecho para melhor mostrar este caso, o resultado mostrado no console para este demo, modifique isso de acordo com sua necessidade. Tentei otimizá-lo o melhor que pude sem comprometer o desempenho, no entanto, comprometeu alguma legibilidade humana. Ele aproveita que os arrays são objetos e as variáveis que apontam para os arrays são mantidas como referência. Nenhum figurão para este, fique à vontade.
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}), {});
versão principal es2018
O método FormData .entries
e a for of
expressão não são suportados no IE11 e Safari.
Aqui está uma versão mais simples para suportar Safari, Chrome, Firefox e Edge
function formDataToJSON(formElement) {
var formData = new FormData(formElement),
convertedJSON = {};
formData.forEach(function(value, key) {
convertedJSON[key] = value;
});
return convertedJSON;
}
Aviso: esta resposta não funciona no IE11.
FormData não tem um forEach
método no IE11.
Ainda estou procurando uma solução final para oferecer suporte a todos os principais navegadores.
Se você estiver usando lodash, isso pode ser feito de forma concisa com fromPairs
import {fromPairs} from 'lodash';
const object = fromPairs(Array.from(formData.entries()));
Se você precisar de suporte para serializar campos aninhados, semelhante a como o PHP lida com campos de formulário, você pode usar a seguinte função
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>
Você pode tentar isso
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);
}
Mesmo que a resposta de @dzuc já seja muito boa, você pode usar a desestruturação de array (disponível em navegadores modernos ou com o Babel) para torná-la ainda mais 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,
}), {})
One-liner abusivo!
Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});
Hoje eu aprendi que o firefox tem suporte a propagação de objetos e desestruturação de array!
Se os itens a seguir atendem às suas necessidades, você está com sorte:
[['key','value1'], ['key2','value2']
(como o que FormData oferece) em um objeto chave-> valor como {key1: 'value1', key2: 'value2'}
e convertê-lo em uma string JSON.Aqui está o código de que você precisa:
const data = new FormData(document.querySelector('form'));
const json = JSON.stringify(Array.from(data).reduce((o,[k,v])=>(o[k]=v,o),{}));
Espero que isso ajude alguém.
Não vi menções ao método FormData.getAll até agora.
Além de retornar todos os valores associados a uma determinada chave de dentro de um objeto FormData, torna-se muito simples usar o método Object.fromEntries conforme especificado por outros aqui.
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 em ação
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>
Funcionou para mim
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>
ou<input type="checkbox">
No meu caso, os dados eram dados, a base de incêndio esperava um objeto, mas os dados contêm o objeto, bem como todos os outros materiais, então tentei data.value funcionou !!!
Estou chegando tarde aqui. No entanto, fiz um método simples que verifica a entrada type = "caixa de seleção"
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 isso ajude mais alguém.
Você pode fazer esse trabalho facilmente, sem usar nada especial. Um código como o abaixo 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()
ajuda? Talvez você tente consertar algo que pode ser feito de outra maneira?