Converter dados de formulário em objeto JavaScript com jQuery

1624

Como converter todos os elementos do meu formulário em um objeto JavaScript?

Eu gostaria de ter alguma maneira de criar automaticamente um objeto JavaScript do meu formulário, sem ter que fazer um loop sobre cada elemento. Eu não quero uma string, como retornado por $('#formid').serialize();, nem o mapa retornado por$('#formid').serializeArray();

Yisroel
fonte
16
porque o primeiro retorna uma string, exatamente como o que você obteria se enviasse o formulário com um método GET, e o segundo fornece uma matriz de objetos, cada um com um par de nome e valor. Quero que, se eu tiver um campo chamado "email", recebo um objeto que permita recuperar esse valor com obj.email. Com serializeArray (), eu teria que fazer algo como obj [indexOfElement] .value
Yisrael
2
@ James - A resposta aceita usando a biblioteca JSON-js de D. Crockford. Aqui está um exemplo: github.com/tleese22/google-app-engine-jappstart/blob/master/src/…
Taylor Leese
4
@ Taylor Sim, eu diria que a resposta correta usa a função lib e Tobias de Crockford da seguinte maneira: JSON.stringify ($ ('myForm'). SerializeObject ())
James McCormack
5
@ Jonz - Existem outros motivos além do envio / transmissão para o uso de um elemento do formulário. Se você estiver fazendo algum trabalho pesado com os valores do formulário no JavaScript (por exemplo, aplicativo de página única), é muito útil tê-los em um formato de objeto para acessar e manipular. Além disso, as strings de consulta HTTP Post e Get não são os únicos formatos para mover dados.
227 Patrick M
3
Um bom js me deparei >> github.com/marioizquierdo/jquery.serializeJSON
Bongs

Respostas:

1657

serializeArrayjá faz exatamente isso. Você só precisa massagear os dados no formato necessário:

function objectifyForm(formArray) {//serialize data function

  var returnArray = {};
  for (var i = 0; i < formArray.length; i++){
    returnArray[formArray[i]['name']] = formArray[i]['value'];
  }
  return returnArray;
}

Cuidado com os campos ocultos que têm o mesmo nome que as entradas reais, pois eles serão substituídos.

Tobias Cohen
fonte
4
como tvanfosson diz, por que iterar a coleção duas vezes?
218 Yisroel
69
Você quer dizer "por que usar serializeArray para obter os dados em primeiro lugar?" Como o serializeArray já está gravado, é testado em vários navegadores e, teoricamente, poderia ser aprimorado em versões posteriores do jQuery. Quanto menos código você escrever e precisar acessar diretamente coisas inconsistentes, como elementos DOM, mais estável será o seu código.
Tobias Cohen
56
Esteja avisado, serializeArray () não incluirá elementos desativados. Costumo desabilitar elementos de entrada que são sincronizados com outros elementos da página, mas ainda os quero incluídos no meu objeto serializado. É melhor usar algo como $.map( $("#container :input"), function(n, i) { /* n.name and $(n).val() */ } );se você precisar incluir elementos desabilitados.
Samuel Meacham
22
@TobiasCohen Ele não manipula foo[bar]entradas de tipo conforme o esperado, sem mencionar a maioria das outras variedades de nomes de entrada. Depois de ficar muito frustrado com soluções superficiais para esse problema, acabei escrevendo meu próprio plugin jQuery - detalhes na resposta que forneci a esta pergunta.
7111
6
@macek Eu sei que isso tem alguns meses, mas desde quando as matrizes usam índices não numéricos? Ninguém deve nomear uma entrada foo [bar] e esperar tratá-la como uma matriz. Você está confundindo matrizes e hashes? Sim, geralmente [] é entendido como um acessador, mas não apenas para matrizes. Dizer também que é HTML válido, mas não na especificação HTML, é uma contradição. Sim, o navegador pode não se engasgar com ele, mas poucos servidores da web saberão desserializar isso como se fossem uma matriz. Por quê? Porque não está na especificação HTML. Portanto, é de fato inválido.
kroehre
446

Converter formulários em JSON como um chefe


A fonte atual está no GitHub e no Bower .

$ bower install jquery-serialize-object


O código a seguir agora está obsoleto .

O código a seguir pode funcionar com todos os tipos de nomes de entrada; e lide com eles exatamente como você esperaria.

Por exemplo:

<!-- All of these will work! -->
<input name="honey[badger]" value="a">
<input name="wombat[]" value="b">
<input name="hello[panda][]" value="c">
<input name="animals[0][name]" value="d">
<input name="animals[0][breed]" value="e">
<input name="crazy[1][][wonky]" value="f">
<input name="dream[as][vividly][as][you][can]" value="g">
// Output
{
  "honey":{
    "badger":"a"
  },
  "wombat":["b"],
  "hello":{
    "panda":["c"]
  },
  "animals":[
    {
      "name":"d",
      "breed":"e"
    }
  ],
  "crazy":[
    null,
    [
      {"wonky":"f"}
    ]
  ],
  "dream":{
    "as":{
      "vividly":{
        "as":{
          "you":{
            "can":"g"
          }
        }
      }
    }
  }
}

Uso

$('#my-form').serializeObject();

A Feitiçaria (JavaScript)

(function($){
    $.fn.serializeObject = function(){

        var self = this,
            json = {},
            push_counters = {},
            patterns = {
                "validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/,
                "key":      /[a-zA-Z0-9_]+|(?=\[\])/g,
                "push":     /^$/,
                "fixed":    /^\d+$/,
                "named":    /^[a-zA-Z0-9_]+$/
            };


        this.build = function(base, key, value){
            base[key] = value;
            return base;
        };

        this.push_counter = function(key){
            if(push_counters[key] === undefined){
                push_counters[key] = 0;
            }
            return push_counters[key]++;
        };

        $.each($(this).serializeArray(), function(){

            // Skip invalid keys
            if(!patterns.validate.test(this.name)){
                return;
            }

            var k,
                keys = this.name.match(patterns.key),
                merge = this.value,
                reverse_key = this.name;

            while((k = keys.pop()) !== undefined){

                // Adjust reverse_key
                reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');

                // Push
                if(k.match(patterns.push)){
                    merge = self.build([], self.push_counter(reverse_key), merge);
                }

                // Fixed
                else if(k.match(patterns.fixed)){
                    merge = self.build([], k, merge);
                }

                // Named
                else if(k.match(patterns.named)){
                    merge = self.build({}, k, merge);
                }
            }

            json = $.extend(true, json, merge);
        });

        return json;
    };
})(jQuery);
Macek
fonte
17
Então, isso funciona muito bem. Mas está com o nome errado: não retorna JSON, como o nome indica. Em vez disso, ele retorna um objeto literal. Além disso, é importante verificar se há hasOwnProperty, caso contrário, suas matrizes terão qualquer coisa anexada ao protótipo, como: {numbers: ["1", "3", indexOf: function () {...}]}
frontendbeauty
5
@frontendbeauty, na verdade, toJSON é exatamente o que as especificações dizem que deve ser chamado: developer.mozilla.org/en/JSON#toJSON()_method um nome impróprio infeliz.
Ryan Florence
4
@Marek, eu fiz um teste aqui no jsfiddle . O truque é nomear sua seleção corretamente. <select name="foo" multiple="multiple">não funcionará em nenhum cenário. No entanto, se você usar [], como em <select name="bar[]" multiple="multiple">, ele vai funcionar muito bem :)
Maček
3
Queria ressaltar que github.com/serbanghita/formToObject é um método JavaScript semelhante (não é necessária nenhuma biblioteca de terceiros) que faz o mesmo trabalho. Suporta 'múltiplo', ignora elementos 'desativados'.
Gerban Ghiță 26/09/2013
8
Essa solução deve estar no topo, pois lida com o problema de chaves aninhadas como nomes de elementos de formulário.
SquareCat 23/02
283

O que há de errado com:

var data = {};
$(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); 
mkschreder
fonte
2
@LayZee - se nada for selecionado, por que você o quer no seu back-end? se você precisar selecionar uma opção, valide a entrada antes de serializar.
Dementic
21
se você não está retornando nada, por que você não usa apenas em .eachvez de .map??
Azerafati 6/11
10
É tão simples porque é super ingênuo ... não lida com caixas de seleção com o mesmo nome. Sempre que substituir o item marcado antes dele. Você definitivamente precisará de alguma detecção de tipo de entrada para garantir que seja serializada corretamente.
Joshua F. Rountree
5
Se você quer uma solução jQuery pura, você poderia usarvar form = {}; $.each($(this).serializeArray(), function (i, field) { form[field.name] = field.value || ""; });
tfmontague
42
$(this).serializeArray().reduce(function(m,o){ m[o.name] = o.value; return m;}, {})
locais de
103

Uma versão fixa da solução de Tobias Cohen. Este lida corretamente com valores falsos como 0e ''.

jQuery.fn.serializeObject = function() {
  var arrayData, objectData;
  arrayData = this.serializeArray();
  objectData = {};

  $.each(arrayData, function() {
    var value;

    if (this.value != null) {
      value = this.value;
    } else {
      value = '';
    }

    if (objectData[this.name] != null) {
      if (!objectData[this.name].push) {
        objectData[this.name] = [objectData[this.name]];
      }

      objectData[this.name].push(value);
    } else {
      objectData[this.name] = value;
    }
  });

  return objectData;
};

E uma versão do CoffeeScript para sua conveniência de codificação:

jQuery.fn.serializeObject = ->
  arrayData = @serializeArray()
  objectData = {}

  $.each arrayData, ->
    if @value?
      value = @value
    else
      value = ''

    if objectData[@name]?
      unless objectData[@name].push
        objectData[@name] = [objectData[@name]]

      objectData[@name].push value
    else
      objectData[@name] = value

  return objectData
Daniel X Moore
fonte
No CoffeeScript, que em primeiro lugar se o bloco pode ser encurtado paravalue = @value ? ''
nzifnab
E se você estiver tentando serializar formulários do tipo Rails, convém remover a raiz do elemento das chaves geradas. Para conseguir isso, eu adicionei um novo param keyMape a seguinte linha: key = if keyMap? then keyMap(@name) else @name. Agora você pode passar a função de mapeamento como (name) -> name.match(/\[([^\]]+)]/)[1]. E, em seguida, seria preciso mudar tudo subseqüente @namepara key, é claro
Damir Zekic
@ DamirZekić, se o seu elemento raiz era post, você poderia simplesmente fazer $('form').serializeObject().post. Não há necessidade de mapeamento sofisticado.
21412
@ DamirZekić O que esta linha está fazendo? if (!objectData[this.name].push)??
Kittu
@kittu Ele está verificando se objectData[this.name]o método push (a grosso modo, é uma matriz). Se for uma matriz, ele empurra o valor; se não for uma matriz, o converte em uma matriz, para que vários valores com a mesma chave sejam combinados em uma matriz.
Daniel X Moore
63

Eu gosto de usar Array.prototype.reduceporque é uma linha e não depende do Underscore.js ou similar:

$('#formid').serializeArray()
    .reduce(function(a, x) { a[x.name] = x.value; return a; }, {});

Isso é semelhante à resposta usada Array.prototype.map, mas você não precisa desordenar seu escopo com uma variável de objeto adicional. Balcão único.

NOTA IMPORTANTE : Os formulários com entradas com nameatributos duplicados são HTML válidos e, na verdade, são uma abordagem comum. O uso de qualquer uma das respostas neste tópico será inadequado nesse caso (já que as chaves de objetos devem ser únicas).

Ethan Brown
fonte
Isto é bastante elegante. Mas vale a pena notar que array.prototype.reduce()não está disponível no IE8, pois faz parte da especificação do ECMAScript 5. Portanto, se você precisar do suporte ao IE8, convém usar um polyfill ou outra solução.
gfullam
3
É verdade, mas é fácil o suficiente para o polyfill. Além disso, a dor de cabeça do IE8 é quase acabando, graças ao excelente trabalho da Microsoft com o IE11 Enterprise Mode - não atendo mais usuários individuais com o IE8, mas quando uma organização com 10.000 funcionários que todos usam o IE8 aparece ... isso é diferente. Felizmente, a Microsoft está trabalhando duro nesse problema.
Ethan Brown
1
Bem, você deve fazer com que seu pessoal de TI analise o modo IE11 Enterprise - ele fornece um navegador moderno E uma maneira de executar aplicativos compatíveis com o IE8. Movimento inteligente da parte da Microsoft: howtogeek.com/184634/…
Ethan Brown
30

Todas essas respostas me pareciam exageradas. Há algo a ser dito para simplificar. Contanto que todas as entradas de seu formulário tenham o atributo name definido, isso deve funcionar apenas jim dandy.

$('form.myform').submit(function () {
  var $this = $(this)
    , viewArr = $this.serializeArray()
    , view = {};

  for (var i in viewArr) {
    view[viewArr[i].name] = viewArr[i].value;
  }

  //Do stuff with view object here (e.g. JSON.stringify?)
});
Jeffrey Van Alstine
fonte
4
No entanto, não processa dados de formulário aninhados, é por isso que as respostas ficam mais complicadas.
Frontendbeauty
Funcionou muito bem para mim. Armazenamento necessário apenas de pares de valores-chave; restringir e depois salvar em localStorage ou cookie funcionou apenas com dandy.
precisa saber é o seguinte
23

Realmente não há como fazer isso sem examinar cada um dos elementos. O que você realmente quer saber é "alguém já escreveu um método que converte um formulário em um objeto JSON?" Algo como o seguinte deve funcionar - observe que ele fornecerá apenas os elementos do formulário que seriam retornados por meio de um POST (deve ter um nome). Isso não foi testado .

function formToJSON( selector )
{
     var form = {};
     $(selector).find(':input[name]:enabled').each( function() {
         var self = $(this);
         var name = self.attr('name');
         if (form[name]) {
            form[name] = form[name] + ',' + self.val();
         }
         else {
            form[name] = self.val();
         }
     });

     return form;
}
tvanfosson
fonte
verdade, eu adoraria um plugin que faria isso por mim. a limitação de ter um nome não é grande coisa. isso puxará todos os campos de um formulário, incluindo áreas de texto e seleções?
226 Yisroel
não tenho certeza se você deseja contabilizar [desativado], mas acho que não deve ser enviado / retirado.
meder omuraliev
pode ser mais simples de usar serializeArray () para obter o mapa, e, em seguida, usar código semelhante ao acima para convertê-lo um objeto JSON normal, desta forma eu não estou lidando com o próprio formulário
Yisrael
2
Usar o serializedArray funcionaria, mas essencialmente você repetiria a coleção duas vezes - uma vez para produzir a matriz e depois sobre a matriz. Não vejo a necessidade disso.
tvanfosson
Ajustarei o seletor para ativado / desativado.
tvanfosson
23

Se você estiver usando o Underscore.js, poderá usar o relativamente conciso:

_.object(_.map($('#myform').serializeArray(), _.values))
olleicua
fonte
19

Eu verifiquei que há um problema com todas as outras respostas, que, se o nome da entrada for como uma matriz, como name[key], então, ele deve ser gerado assim:

name:{ key : value }


Por exemplo: Se você possui um formulário HTML semelhante ao abaixo:

<form>
    <input name="name" value="value" >
    <input name="name1[key1]" value="value1" >
    <input name="name2[key2]" value="value2" >
    <input name="name3[key3]" value="value3" >
</form>

Mas deve ser gerado exatamente como o JSON abaixo e não se torna um objeto como o seguinte com todas as outras respostas. Portanto, se alguém quiser trazer algo como o seguinte JSON, tente o código JS abaixo.

{
    name  : 'value',
    name1 : { key1 : 'value1' },
    name2 : { key2 : 'value2' },
    name3 : { key2 : 'value2' }
}

$.fn.getForm2obj = function() {
  var _ = {};
  $.map(this.serializeArray(), function(n) {
    const keys = n.name.match(/[a-zA-Z0-9_]+|(?=\[\])/g);
    if (keys.length > 1) {
      let tmp = _;
      pop = keys.pop();
      for (let i = 0; i < keys.length, j = keys[i]; i++) {
        tmp[j] = (!tmp[j] ? (pop == '') ? [] : {} : tmp[j]), tmp = tmp[j];
      }
      if (pop == '') tmp = (!Array.isArray(tmp) ? [] : tmp), tmp.push(n.value);
      else tmp[pop] = n.value;
    } else _[keys.pop()] = n.value;
  });
  return _;
}
console.log($('form').getForm2obj());
$('form input').change(function() {
  console.clear();
  console.log($('form').getForm2obj());
});
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<form>
  <input name="name" value="value">
  <input type="checkbox" name="name1[]" value="1" checked="checked">1
  <input type="checkbox" name="name1[]" value="2">2
  <input type="checkbox" name="name1[]" value="3">3<br>
  <input type="radio" name="gender" value="male" checked="checked">male
  <input type="radio" name="gender" value="female"> female
  <input name="name2[key1]" value="value1">
  <input name="one[another][another_one]" value="value4">
  <input name="name3[1][name]" value="value4">
  <input name="name3[2][name]" value="value4">
  <input name="[]" value="value5">
</form>

Bhavik Hirani
fonte
Essa resposta não cobrir o caso mencionado, mas não cobre casos como checkbox [] ou mesmo um [outro] [another_one]
Leonardo Beal
1
@LeonardoBeal eu conserto meus ans .. verifique isso agora ..!
Bhavik Hirani
Eu sou o defensor do voto negativo e votei com voto negativo porque evalnão deve ser usado dessa maneira porque eval, quando usado dessa maneira, promove práticas terrivelmente não confiáveis, com erros, com desempenho incorreto e potencialmente inseguras.
Jack Giffin
2
Não posso concordar que esta seja uma boa resposta. E, por favor, ao escrever as respostas, torne seu código auto-explicativo ou explique-o. this.c = function(k,v){ eval("c = typeof "+k+";"); if(c == 'undefined') _t.b(k,v);}é curto e não explicativo. Um desenvolvedor com menos experiência apenas copiará isso sem entender por que e como funciona.
iRaS 27/11/19
2
@JackGiffin Confira meu novo código agora porque removi eval()meu código.
Bhavik Hirani
17

Ok, eu sei que isso já tem uma resposta altamente votada, mas outra pergunta semelhante foi feita recentemente e eu fui direcionado para essa pergunta também. Também gostaria de oferecer minha solução, porque oferece uma vantagem sobre a solução aceita: você pode incluir elementos de formulário desabilitados (o que às vezes é importante, dependendo de como a interface do usuário funciona)

Aqui está a minha resposta da outra pergunta SO :

Inicialmente, estávamos usando o serializeArray()método jQuery , mas isso não inclui elementos de formulário que estão desabilitados. Geralmente, desabilitamos elementos de formulário que são "sincronizados" com outras fontes na página, mas ainda precisamos incluir os dados em nosso objeto serializado. Então serializeArray()está fora. Usamos o :inputseletor para obter todos os elementos de entrada (ativados e desativados) em um determinado contêiner e depois $.map()criar nosso objeto.

var inputs = $("#container :input");
var obj = $.map(inputs, function(n, i)
{
    var o = {};
    o[n.name] = $(n).val();
    return o;
});
console.log(obj);

Observe que, para que isso funcione, cada uma de suas entradas precisará de um nameatributo, que será o nome da propriedade do objeto resultante.

Na verdade, isso é ligeiramente modificado do que usamos. Precisávamos criar um objeto estruturado como um .NET IDictionary, por isso usamos o seguinte: (eu forneço aqui caso seja útil)

var obj = $.map(inputs, function(n, i)
{
    return { Key: n.name, Value: $(n).val() };
});
console.log(obj);

Gosto dessas duas soluções, porque são simples usos da $.map()função e você tem controle total sobre o seletor (portanto, quais elementos você acaba incluindo no objeto resultante). Além disso, nenhum plug-in extra é necessário. JQuery antigo simples.

Samuel Meacham
fonte
6
Eu tentei isso em um projeto, usando mapassim cria uma matriz de objetos com uma única propriedade, não recolhe as propriedades em um único objeto.
Joshperry 01/10/10
16

Essa função deve manipular matrizes multidimensionais junto com vários elementos com o mesmo nome.

Eu tenho usado por alguns anos até agora:

jQuery.fn.serializeJSON=function() {
  var json = {};
  jQuery.map(jQuery(this).serializeArray(), function(n, i) {
    var _ = n.name.indexOf('[');
    if (_ > -1) {
      var o = json;
      _name = n.name.replace(/\]/gi, '').split('[');
      for (var i=0, len=_name.length; i<len; i++) {
        if (i == len-1) {
          if (o[_name[i]]) {
            if (typeof o[_name[i]] == 'string') {
              o[_name[i]] = [o[_name[i]]];
            }
            o[_name[i]].push(n.value);
          }
          else o[_name[i]] = n.value || '';
        }
        else o = o[_name[i]] = o[_name[i]] || {};
      }
    }
    else {
      if (json[n.name] !== undefined) {
        if (!json[n.name].push) {
          json[n.name] = [json[n.name]];
        }
        json[n.name].push(n.value || '');
      }
      else json[n.name] = n.value || '';      
    }
  });
  return json;
};
Sergey Varlamov
fonte
15

Você consegue fazer isso:

var frm = $(document.myform);
var data = JSON.stringify(frm.serializeArray());

Veja JSON .

Harini Sekar
fonte
14

O one-liner (sem dependências além do jQuery) usa a ligação de objeto fixo para a função transmitida ao mapmétodo.

$('form').serializeArray().map(function(x){this[x.name] = x.value; return this;}.bind({}))[0]

O que faz?

"id=2&value=1&comment=ok" => Object { id: "2", value: "1", comment: "ok" }

adequado para aplicativos da web progressivos (é possível suportar facilmente ações de envio de formulários regulares e solicitações de ajax)

test30
fonte
12

Usar:

function form_to_json (selector) {
  var ary = $(selector).serializeArray();
  var obj = {};
  for (var a = 0; a < ary.length; a++) obj[ary[a].name] = ary[a].value;
  return obj;
}

Resultado:

{"myfield": "myfield value", "passwordfield": "mypasswordvalue"}
Adrian Seeley
fonte
7

Simplicidade é melhor aqui. Eu usei uma string simples substituir por uma expressão regular, e eles funcionaram como um encanto até agora. Não sou especialista em expressões regulares, mas aposto que você pode até preencher objetos muito complexos.

var values = $(this).serialize(),
attributes = {};

values.replace(/([^&]+)=([^&]*)/g, function (match, name, value) {
    attributes[name] = value;
});
ngr
fonte
7

De alguma resposta antiga :

$('form input, form select').toArray().reduce(function(m,e){m[e.name] = $(e).val(); return m;},{})
juanpastas
fonte
Pelo que sei, a diferença é que sua solução não depende, serializeArrayportanto você tem a liberdade de escolher as entradas que deseja (por exemplo, você pode incluir entradas desativadas), certo? Ou seja, isso não está associado a nenhum formulário ou ao evento de envio, é apenas independente por si só?
Dvtan 27/05
a única pequena diferença com a resposta vinculada é que não há dados necessários para instanciar, reduceretorna o objeto. Isso não é independente, pois toArrayé do jQuery.
sites
6

Encontrei um problema com o código de Tobias Cohen (não tenho pontos suficientes para comentar diretamente), o que de outra forma funciona para mim. Se você tiver duas opções de seleção com o mesmo nome, ambas com value = "", o código original produzirá "name": "" em vez de "name": ["", ""]

Acho que isso pode ser corrigido adicionando "|| o [this.name] == ''" à primeira condição if:

$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] || o[this.name] == '') {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};
user1134789
fonte
6

Usando a solução de maček , modifiquei-a para trabalhar com a maneira como o ASP.NET MVC lida com objetos aninhados / complexos no mesmo formulário. Tudo o que você precisa fazer é modificar a parte de validação para isso:

"validate": /^[a-zA-Z][a-zA-Z0-9_]*((?:\[(?:\d*|[a-zA-Z0-9_]+)\])*(?:\.)[a-zA-Z][a-zA-Z0-9_]*)*$/,

Isso corresponderá e, em seguida, mapeará corretamente os elementos com nomes como:

<input type="text" name="zooName" />

E

<input type="text" name="zooAnimals[0].name" />
G-Ram
fonte
5

a maneira mais simples e mais precisa que eu encontrei para este problema foi usar plug-in churrasco ou este um (que é de cerca de 0,5K tamanho bytes).

Ele também trabalha com matrizes multidimensionais.

$.fn.serializeObject = function()
{
	return $.deparam(this.serialize());
};

Roey
fonte
Isso parece funcionar bem. Existe um repositório alternativo para o jquery-deparam que inclui arquivos de descrição para bower e npm.
Alf Eaton
4

Existe um plugin para fazer exatamente isso no jQuery, jquery.serializeJSON . Eu usei com sucesso em alguns projetos agora. Ele funciona como um encanto.

Peter Mortensen
fonte
4
const formData = new FormData(form);

let formDataJSON = {};

for (const [key, value] of formData.entries()) {

    formDataJSON[key] = value;
}
Stonetip
fonte
3

Eu prefiro essa abordagem porque: você não precisa iterar mais de duas coleções, pode obter coisas diferentes de "nome" e "valor", se necessário, e pode limpar seus valores antes de armazená-los no objeto ( se você tiver valores padrão que não deseja armazenar, por exemplo).

$.formObject = function($o) {
    var o = {},
        real_value = function($field) {
            var val = $field.val() || "";

            // additional cleaning here, if needed

            return val;
        };

    if (typeof o != "object") {
        $o = $(o);
    }

    $(":input[name]", $o).each(function(i, field) {
        var $field = $(field),
            name = $field.attr("name"),
            value = real_value($field);

        if (o[name]) {
            if (!$.isArray(o[name])) {
                o[name] = [o[name]];
            }

            o[name].push(value);
        }

        else {
            o[name] = value;
        }
    });

    return o;
}

Use assim:

var obj = $.formObject($("#someForm"));

Testado apenas no Firefox.

kflorence
fonte
3

Transforme qualquer coisa em um objeto (não testado em unidade)

<script type="text/javascript">
string = {};

string.repeat = function(string, count)
{
    return new Array(count+1).join(string);
}

string.count = function(string)
{
    var count = 0;

    for (var i=1; i<arguments.length; i++)
    {
        var results = string.match(new RegExp(arguments[i], 'g'));
        count += results ? results.length : 0;
    }

    return count;
}

array = {};

array.merge = function(arr1, arr2)
{
    for (var i in arr2)
    {
        if (arr1[i] && typeof arr1[i] == 'object' && typeof arr2[i] == 'object')
            arr1[i] = array.merge(arr1[i], arr2[i]);
        else
            arr1[i] = arr2[i]
    }

    return arr1;
}

array.print = function(obj)
{
    var arr = [];
    $.each(obj, function(key, val) {
        var next = key + ": ";
        next += $.isPlainObject(val) ? array.print(val) : val;
        arr.push( next );
      });

    return "{ " +  arr.join(", ") + " }";
}

node = {};

node.objectify = function(node, params)
{
    if (!params)
        params = {};

    if (!params.selector)
        params.selector = "*";

    if (!params.key)
        params.key = "name";

    if (!params.value)
        params.value = "value";

    var o = {};
    var indexes = {};

    $(node).find(params.selector+"["+params.key+"]").each(function()
    {
        var name = $(this).attr(params.key),
            value = $(this).attr(params.value);

        var obj = $.parseJSON("{"+name.replace(/([^\[]*)/, function()
        {
            return '"'+arguments[1]+'"';
        }).replace(/\[(.*?)\]/gi, function()
        {
            if (arguments[1].length == 0)
            {
                var index = arguments[3].substring(0, arguments[2]);
                indexes[index] = indexes[index] !== undefined ? indexes[index]+1 : 0;

                return ':{"'+indexes[index]+'"';
            }
            else
                return ':{"'+escape(arguments[1])+'"';
        })+':"'+value.replace(/[\\"]/gi, function()
        {
            return "\\"+arguments[0]; 
        })+'"'+string.repeat('}', string.count(name, ']'))+"}");

        o = array.merge(o, obj);
    });

    return o;
}
</script>

A saída do teste:

$(document).ready(function()
{
    console.log(array.print(node.objectify($("form"), {})));
    console.log(array.print(node.objectify($("form"), {selector: "select"})));
});

em

<form>
    <input name='input[a]' type='text' value='text'/>
    <select name='input[b]'>
        <option>select</option>
    </select>

    <input name='otherinput[c][a]' value='a'/>
    <input name='otherinput[c][]' value='b'/>
    <input name='otherinput[d][b]' value='c'/>
    <input name='otherinput[c][]' value='d'/>

    <input type='hidden' name='anotherinput' value='hidden'/>
    <input type='hidden' name='anotherinput' value='1'/>

    <input type='submit' value='submit'/>
</form>

produzirá:

{ input: { a: text, b: select }, otherinput: { c: { a: a, 0: b, 1: d }, d: { b: c } }, anotherinput: 1 }
{ input: { b: select } }
eithed
fonte
3

Encontrei um problema com a solução selecionada.

Ao usar formulários com nomes baseados em array, a função serializeArray () do jQuery realmente morre.

Eu tenho uma estrutura PHP que usa nomes de campos baseados em matriz para permitir que o mesmo formulário seja colocado na mesma página várias vezes em vários modos de exibição. Isso pode ser útil para adicionar, editar e excluir na mesma página sem modelos de formulário conflitantes.

Como eu queria seralizar os formulários sem precisar remover essa funcionalidade básica absoluta, decidi escrever meu próprio seralizeArray ():

        var $vals = {};

        $("#video_edit_form input").each(function(i){
            var name = $(this).attr("name").replace(/editSingleForm\[/i, '');

            name = name.replace(/\]/i, '');

            switch($(this).attr("type")){
                case "text":
                    $vals[name] = $(this).val();
                    break;
                case "checkbox":
                    if($(this).attr("checked")){
                        $vals[name] = $(this).val();
                    }
                    break;
                case "radio":
                    if($(this).attr("checked")){
                        $vals[name] = $(this).val();
                    }
                    break;
                default:
                    break;
            }
        });

Observação: isso também funciona fora do formulário submit (); portanto, se ocorrer um erro no restante do código, o formulário não será enviado se você inserir um botão de link dizendo "salvar alterações".

Observe também que essa função nunca deve ser usada para validar o formulário apenas para reunir os dados a serem enviados ao servidor para validação. Usar esse código fraco e atribuído em massa causará XSS , etc.

Sammaye
fonte
3

Eu tive o mesmo problema recentemente e criei este plugin .toJSON jQuery que converte um formulário em um objeto JSON com a mesma estrutura. Isso também é especialmente útil para formulários gerados dinamicamente, nos quais você deseja permitir que o usuário adicione mais campos em locais específicos.

O ponto é que você pode realmente criar um formulário para que ele tenha uma estrutura própria; digamos que você queira criar um formulário no qual o usuário insira seus lugares favoritos na cidade: você pode imaginar esse formulário para representar um <places>...</places>elemento XML contendo um lista de locais que o usuário gosta, portanto, uma lista de <place>...</place>elementos, cada um contendo, por exemplo, um <name>...</name>elemento, um <type>...</type>elemento e, em seguida, uma lista de <activity>...</activity>elementos para representar as atividades que você pode executar em um local como esse. Portanto, sua estrutura XML seria assim:

<places>

    <place>

        <name>Home</name>
        <type>dwelling</type>

        <activity>sleep</activity>
        <activity>eat</activity>
        <activity>watch TV</activity>

    </place>

    <place>...</place>

    <place>...</place>

</places>

Quão legal seria ter um objeto JSON disso, que representaria essa estrutura exata, para que você seja capaz de:

  • Armazene esse objeto como em qualquer banco de dados semelhante ao CouchDB
  • Leia-o no lado do servidor $ _POST [] e recupere uma matriz aninhada corretamente que você pode manipular semanticamente
  • Use algum script do lado do servidor para convertê-lo em um arquivo XML bem formado (mesmo que você não conheça a estrutura exata a priori)
  • De alguma forma, use-o como em qualquer script de servidor semelhante ao Node.js.

OK, agora precisamos pensar em como um formulário pode representar um arquivo XML.

É claro que a <form>tag é o root, mas temos o <place>elemento que é um contêiner e não o próprio elemento de dados; portanto, não podemos usar uma tag de entrada para ele.

Aqui é onde a <fieldset>tag é útil! Usaremos <fieldset>tags para representar todos os elementos do contêiner em nosso formulário / representação XML e, assim, obter um resultado como este:

<form name="places">

    <fieldset name="place">

        <input type="text" name="name"/>
        <select name="type">
            <option value="dwelling">Dwelling</option>
            <option value="restoration">Restoration</option>
            <option value="sport">Sport</option>
            <option value="administrative">Administrative</option>
        </select>

        <input type="text" name="activity"/>
        <input type="text" name="activity"/>
        <input type="text" name="activity"/>

    </fieldset>

</form>

Como você pode ver neste formulário, estamos violando a regra dos nomes exclusivos, mas tudo bem, porque eles serão convertidos em uma matriz de elemento e, portanto, serão referenciados apenas pelo índice dentro da matriz.

Nesse ponto, você pode ver como não existe name="array[]"um nome semelhante no formulário e tudo é bonito, simples e semântico.

Agora queremos que este formulário seja convertido em um objeto JSON que se parecerá com isso:

{'places':{

    'place':[

        {

            'name': 'Home',
            'type': 'dwelling',

            'activity':[

                 'sleep',
                 'eat',
                 'watch TV'

            ]

        },

        {...},

        {...}

    ]

}}

Para fazer isso, desenvolvi este plugin jQuery aqui, que alguém ajudou a otimizar neste segmento de Revisão de código e fica assim:

$.fn.toJSO = function () {
    var obj = {},
        $kids = $(this).children('[name]');
    if (!$kids.length) {
        return $(this).val();
    }
    $kids.each(function () {
        var $el = $(this),
            name = $el.attr('name');
        if ($el.siblings("[name=" + name + "]").length) {
            if (!/radio|checkbox/i.test($el.attr('type')) || $el.prop('checked')) {
                obj[name] = obj[name] || [];
                obj[name].push($el.toJSO());
            }
        } else {
            obj[name] = $el.toJSO();
        }
    });
    return obj;
};

Eu também fiz este post para explicar mais isso.

Isso converte tudo em um formulário para JSON (mesmo caixas de seleção e rádio) e tudo o que você precisa fazer é chamar

$.post('script.php',('form').toJSO(), ...);

Eu sei que há muitas maneiras de formas converter em objetos JSON e seguro .serialize()e .serializeArray()trabalho grande na maioria dos casos e destinam-se principalmente a ser utilizado, mas eu acho que toda essa ideia de escrever um formulário como uma estrutura XML com nomes significativos e convertê-lo em um objeto JSON bem formado vale a pena tentar, também o fato de poder adicionar tags de entrada com o mesmo nome sem se preocupar é muito útil se você precisar recuperar dados de formulários gerados dinamicamente.

Espero que isso ajude alguém!

Onheiron
fonte
O que você está tentando fazer?
Onheiron 02/09
3

Eu mesmo codifiquei um formulário para um objeto JavaScript multidimensional para usá-lo na produção. O resultado é https://github.com/serbanghita/formToObject.js .

serbanghita
fonte
Eu usei uma variação disso para uma implementação muito específica, muito obrigado!
Chris Baker
Obrigado por me informar que é útil. Eu tenho alguns recursos recebidos para rolar e isso me motiva.
banerban Ghiță
3

Outra resposta

document.addEventListener("DOMContentLoaded", function() {
  setInterval(function() {
    var form = document.getElementById('form') || document.querySelector('form[name="userprofile"]');
    var json = Array.from(new FormData(form)).map(function(e,i) {this[e[0]]=e[1]; return this;}.bind({}))[0];
    
    console.log(json)
    document.querySelector('#asJSON').value = JSON.stringify(json);
  }, 1000);
})
<form name="userprofile" id="form">
  <p>Name <input type="text" name="firstname" value="John"/></p>
  <p>Family name <input name="lastname" value="Smith"/></p>
  <p>Work <input name="employment[name]" value="inc, Inc."/></p>
  <p>Works since <input name="employment[since]" value="2017" /></p>
  <p>Photo <input type="file" /></p>
  <p>Send <input type="submit" /></p>
</form>

JSON: <textarea id="asJSON"></textarea>

FormData: https://developer.mozilla.org/en-US/docs/Web/API/FormData

test30
fonte
3

[ATUALIZAÇÃO 2020]

Com um oneliner simples no vanilla js:

Object.fromEntries(new FormData(form))
aret
fonte
2

Eu gosto da versão samuels, mas acredito que tenha um pequeno erro. Normalmente JSON é enviado como

{"coreSKU": "PCGUYJS", "name_de": "tanto faz", ...

NÃO como

[{"coreSKU": "PCGUYJS"}, {"name_de": "tanto faz"}, ...

então a função IMO deve ler:

App.toJson = function( selector ) {
    var o = {};
    $.map( $( selector ), function( n,i )
    {
        o[n.name] = $(n).val();
    });     
    return o;
}

e envolvê-lo em uma matriz de dados (como normalmente também é esperado) e, finalmente, enviá-lo como um App.stringify ({data: App.toJson ('#cropform: input')})

Para a stringify, consulte a Pergunta 3593046 para a versão lean, em json2.js para a versão com todas as eventualidades. Isso deve cobrir tudo :)

Frank Nocke
fonte
Obrigado .. isso faz (como você mencionou) uma diferença pequena, mas muito importante.
Adarsha
2

Para uma solução rápida e moderna, use o plugin JSONify jQuery. O exemplo abaixo é retirado literalmente do GitHub README. Todo o crédito a Kushal Pandya, autor do plugin.

Dado:

<form id="myform">
    <label>Name:</label>
    <input type="text" name="name"/>
    <label>Email</label>
    <input type="text" name="email"/>
    <label>Password</label>
    <input type="password" name="password"/>
</form>

Corrida:

$('#myform').jsonify();

Produz:

{"name":"Joe User","email":"[email protected]","password":"mypass"}

Se você deseja executar um jQuery POST com este objeto JSON:

$('#mybutton').click(function() {
    $.post('/api/user', JSON.stringify($('#myform').jsonify()));
}
Jim Stewart
fonte