Convierta datos de formulario a objetos JavaScript con jQuery

1624

¿Cómo convierto todos los elementos de mi formulario en un objeto JavaScript?

Me gustaría tener alguna forma de crear automáticamente un objeto JavaScript a partir de mi formulario, sin tener que recorrer cada elemento. No quiero una cadena, como la devuelta $('#formid').serialize();, ni quiero que el mapa sea devuelto por$('#formid').serializeArray();

Yisroel
fuente
16
porque el primero devuelve una cadena, exactamente como lo que obtendría si enviara el formulario con un método GET, y el segundo le proporciona una matriz de objetos, cada uno con un par de nombre y valor. Quiero que si tengo un campo llamado "correo electrónico" obtengo un objeto que me permitirá recuperar ese valor con obj.email. Con serializeArray (), tendría que hacer algo como obj [indexOfElement] .value
Yisroel el
2
@James: la respuesta aceptada utilizando la biblioteca JSON-js de D. Crockford. Aquí hay un ejemplo: github.com/tleese22/google-app-engine-jappstart/blob/master/src/…
Taylor Leese
44
@ Taylor Sí, diría que la respuesta correcta utiliza la función de Crockford y la función de Tobias de la siguiente manera: JSON.stringify ($ ('myForm'). SerializeObject ())
James McCormack el
55
@ Jonz - Hay otras razones además del envío / transmisión para usar un elemento de formulario. Si está haciendo un trabajo pesado con los valores de formulario dentro de JavaScript (por ejemplo, una aplicación de página única), es muy útil tenerlos en un formato de objeto para acceder y manipular. Además, las cadenas de consulta HTTP Post y Get no son los únicos formatos para mover datos.
Patrick M
3
Un buen js que encontré >> github.com/marioizquierdo/jquery.serializeJSON
Bongs

Respuestas:

1657

serializeArrayYa hace exactamente eso. Solo necesita masajear los datos en el formato requerido:

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

Tenga cuidado con los campos ocultos que tienen el mismo nombre que las entradas reales ya que se sobrescribirán.

Tobias Cohen
fuente
44
Como dice Tvanfosson, ¿por qué recorrer la colección dos veces?
Yisrael
69
¿Quiere decir "por qué usar serializeArray para obtener los datos en primer lugar?" Debido a que serializeArray ya está escrito, se prueba en unidades en múltiples navegadores y, en teoría, podría mejorarse en versiones posteriores de jQuery. Cuanto menos código escriba que tenga acceso directo a elementos inconsistentes como elementos DOM, más estable será su código.
Tobias Cohen
56
Tenga cuidado, serializeArray () no incluirá elementos deshabilitados. A menudo deshabilito los elementos de entrada que se sincronizan con otros elementos en la página, pero todavía quiero que se incluyan en mi objeto serializado. Es mejor usar algo como $.map( $("#container :input"), function(n, i) { /* n.name and $(n).val() */ } );si necesita incluir elementos deshabilitados.
Samuel Meacham
22
@TobiasCohen No maneja foo[bar]entradas de tipo como se esperaba, sin mencionar la mayoría de las otras variedades de nombres de entrada. Después de estar muy frustrado con soluciones superficiales a este problema, terminé escribiendo mi propio complemento jQuery, detalles en la respuesta que proporcioné a esta pregunta.
maček
66
@macek Sé que esto tiene unos meses, pero ¿desde cuándo las matrices usan índices no numéricos? Nadie debería nombrar un input foo [bar] y esperar tratarlo como una matriz. ¿Estás confundiendo matrices y hashes? Sí, [] se entiende comúnmente como un descriptor de acceso, pero no solo para matrices. También decir que es HTML válido pero no en la especificación HTML es una contradicción. Sí, es posible que el navegador no se atragante, pero no muchos servidores web sabrán cómo deserializar eso como lo harían con una matriz. ¿Por qué? Porque no está en la especificación HTML. Por lo tanto, de hecho es inválido.
kroehre
446

Convierte formularios a JSON como un jefe


La fuente actual está en GitHub y Bower .

$ bower install jquery-serialize-object


El siguiente código ahora está en desuso .

El siguiente código puede funcionar con todo tipo de nombres de entrada; y manejarlos como es de esperar.

Por ejemplo:

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

La brujería (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);
maček
fuente
17
Entonces, eso funciona bastante bien. Pero tiene un nombre incorrecto: no devuelve JSON, como su nombre lo indica. En cambio, devuelve un objeto literal. Además, es importante verificar hasOwnProperty, de lo contrario, sus matrices tienen algo adjunto a su prototipo, como: {números: ["1", "3", indexOf: function () {...}]}
frontendbeauty
55
@frontendbeauty en realidad, toJSON es exactamente lo que la especificación dice que debería llamarse: developer.mozilla.org/en/JSON#toJSON()_method un nombre inapropiado desafortunado.
Ryan Florence
44
@Marek, hice una prueba aquí en jsfiddle . El truco es nombrar su selección correctamente. <select name="foo" multiple="multiple">No funcionará en ningún escenario. Sin embargo, si se utiliza [], como en <select name="bar[]" multiple="multiple">, que funciona muy bien :)
Maček
3
Quería señalar que github.com/serbanghita/formToObject es un método JavaScript similar (no se necesitan bibliotecas de terceros) que haga el mismo trabajo. Admite 'múltiples', ignora los elementos 'deshabilitados'.
Șerban Ghiță
8
Esta solución debe estar en la parte superior ya que trata el problema de las claves anidadas como nombres de elementos de formulario.
SquareCat
283

Qué hay de malo en:

var data = {};
$(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); 
mkschreder
fuente
2
@LayZee: si no se selecciona nada, ¿por qué lo querrías en tu back-end? Si debe seleccionar una opción, valide la entrada antes de serializar.
Demenciador
21
si no devuelve nada, ¿por qué no lo usa en .eachlugar de .map??
azerafati
10
Es tan simple porque es súper ingenuo ... no maneja casillas de verificación con el mismo nombre. Cada vez anulará el elemento marcado antes. Definitivamente necesitará un poco de detección de tipo de entrada para garantizar que se serialice correctamente.
Joshua F. Rountree
55
Si desea una solución jQuery pura, puede 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;}, {})
sitios
103

Una versión fija de la solución de Tobias Cohen. Éste maneja correctamente los valores falsos como 0y ''.

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

Y una versión de CoffeeScript para su conveniencia de codificación:

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
fuente
En el coffeescript, eso primero si el bloque se puede acortar avalue = @value ? ''
nzifnab
Y si está intentando serializar formularios similares a Rails, es posible que desee eliminar la raíz del elemento de las claves generadas. Para lograr eso, he añadido un nuevo parámetro keyMapy la siguiente línea: key = if keyMap? then keyMap(@name) else @name. Ahora puede pasar la función de mapeo como (name) -> name.match(/\[([^\]]+)]/)[1]. Y luego uno tendría que cambiar todo después @namede key, por supuesto
Damir Zekić
@ DamirZekić, si su elemento raíz fuera post, simplemente podría hacerlo $('form').serializeObject().post. No hay necesidad de mapeo elegante.
maček
@ DamirZekić ¿Qué está haciendo esta línea? if (!objectData[this.name].push)??
kittu
@kittu Está comprobando si objectData[this.name]tiene el método push (más o menos, es una matriz). Si es una matriz, empuja el valor, si no es una matriz, la convierte en una matriz para que múltiples valores con la misma clave se combinen en una matriz.
Daniel X Moore
63

Me gusta usar Array.prototype.reduceporque es de una sola línea, y no se basa en Underscore.js o similares:

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

Esto es similar al uso de la respuesta Array.prototype.map, pero no necesita saturar su alcance con una variable de objeto adicional. Uno para de comprar.

NOTA IMPORTANTE : Los formularios con entradas que tienen nameatributos duplicados son HTML válidos, y en realidad es un enfoque común. El uso de cualquiera de las respuestas en este hilo será inapropiado en ese caso (ya que las claves de objeto deben ser únicas).

Ethan Brown
fuente
Esto es bastante elegante. Pero vale la pena señalar que array.prototype.reduce()no está disponible en IE8 ya que es parte de la especificación ECMAScript 5. Entonces, si necesita soporte para IE8, querrá usar un polyfill u otra solución por completo.
gfullam
3
Es cierto, pero es bastante fácil de rellenar. Además, el dolor de cabeza de IE8 casi ha terminado, gracias al gran trabajo de Microsoft con IE11 Enterprise Mode: ya no atiendo a usuarios individuales con IE8, pero cuando aparece una organización con 10,000 empleados que usan IE8 ... eso es diferente. Afortunadamente, Microsoft está trabajando duro en ese problema.
Ethan Brown
1
Bueno, debería hacer que su personal de TI mire el modo IE11 Enterprise: proporciona un navegador moderno Y una forma de ejecutar aplicaciones compatibles con IE8. Movimiento
Ethan Brown
30

Todas estas respuestas me parecieron demasiado exageradas. Hay algo que decir por simplicidad. Siempre que todas sus entradas de formulario tengan el atributo de nombre establecido, esto debería funcionar solo con 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
fuente
44
Sin embargo, no maneja datos de formulario anidados, es por eso que las respuestas se vuelven más complicadas.
frontendbeauty
Funcionó muy bien para mí. Solo necesitaba almacenamiento de pares clave-valor; encadenar y luego guardar en localStorage o cookie funcionó de maravilla.
Greg Pettit
23

Realmente no hay forma de hacerlo sin examinar cada uno de los elementos. Lo que realmente quiere saber es "¿alguien más ya ha escrito un método que convierta un formulario en un objeto JSON?" Algo como lo siguiente debería funcionar: tenga en cuenta que solo le dará los elementos del formulario que se devolverían a través de un POST (debe tener un nombre). Esto no está probado .

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
fuente
Es cierto, me encantaría un complemento que haría esto por mí. La limitación de tener un nombre no es gran cosa. ¿Esto extraerá todos los campos de un formulario, incluidas las áreas de texto y las selecciones?
Yisroel
No estoy seguro si desea dar cuenta de [deshabilitado] pero no creo que deba enviarse / recogerse.
meder omuraliev
puede ser más fácil de usar serializeArray () para obtener el mapa, y luego usar un código similar al anterior para convertirla en un objeto JSON normal, así que no estoy tratando con la forma misma
Yisrael
2
Usar serializedArray funcionaría, pero esencialmente estaría iterando sobre la colección dos veces, una para producir la matriz y luego sobre la matriz. No veo la necesidad de eso.
tvanfosson
Ajustaré el selector para habilitar / deshabilitar.
tvanfosson
23

Si está utilizando Underscore.js puede usar el relativamente conciso:

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

Verifiqué que hay un problema con todas las otras respuestas, que si el nombre de entrada es como una matriz, como name[key], entonces debería generarse así:

name:{ key : value }


Por ejemplo: si tiene un formulario HTML similar al siguiente:

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

Pero debe generarse como el JSON a continuación, y no se convierte en un objeto como el siguiente con todas las demás respuestas. Entonces, si alguien quiere traer algo como el siguiente JSON, pruebe el código JS a continuación.

{
    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
fuente
Esta respuesta cubre el caso mencionado, pero no cubre casos como la casilla de verificación [] o incluso uno [otro] [otro_uno]
Leonardo Beal
1
@LeonardoBeal arreglé mi ans .. mira esto ahora ...!
Bhavik Hirani
Soy el votante negativo y voté negativamente porque evalno debería usarse de esta manera porque eval, cuando se usa de esta manera, promueve prácticas terriblemente poco confiables, con errores, de bajo rendimiento y potencialmente inseguras.
Jack Giffin
2
No puedo estar de acuerdo, esta es una buena respuesta. Y cuando escriba las respuestas, haga que su código se explique por sí mismo o explíquelo. this.c = function(k,v){ eval("c = typeof "+k+";"); if(c == 'undefined') _t.b(k,v);}es breve y no explicativo. Un desarrollador con menos experiencia simplemente copiará esto sin entender por qué y cómo funciona.
iRaS
2
@JackGiffin Mira mi nuevo código ahora porque lo eliminé eval().
Bhavik Hirani
17

Ok, sé que esto ya tiene una respuesta altamente votada, pero recientemente se hizo otra pregunta similar , y también me dirigieron a esta pregunta. También me gustaría ofrecer mi solución, ya que ofrece una ventaja sobre la solución aceptada: puede incluir elementos de formulario deshabilitados (que a veces es importante, dependiendo de cómo funcione su interfaz de usuario)

Aquí está mi respuesta de la otra pregunta SO :

Inicialmente, estábamos usando el serializeArray()método de jQuery , pero eso no incluye elementos de formulario que están deshabilitados. A menudo deshabilitaremos elementos de formulario que están "sincronizados" con otras fuentes en la página, pero aún necesitamos incluir los datos en nuestro objeto serializado. Así serializeArray()está fuera. Utilizamos el :inputselector para obtener todos los elementos de entrada (tanto habilitados como deshabilitados) en un contenedor determinado, y luego $.map()para crear nuestro objeto.

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

Tenga en cuenta que para que esto funcione, cada una de sus entradas necesitará un nameatributo, que será el nombre de la propiedad del objeto resultante.

Eso en realidad está ligeramente modificado de lo que usamos. Necesitábamos crear un objeto estructurado como .NET IDictionary, así que utilizamos esto: (lo proporciono aquí en caso de que sea útil)

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

Me gustan ambas soluciones, porque son usos simples de la $.map()función y usted tiene control completo sobre su selector (por lo tanto, qué elementos termina incluyendo en su objeto resultante). Además, no se requiere ningún complemento adicional. Llano viejo jQuery.

Samuel Meacham
fuente
66
Intenté esto en un proyecto, usando mapasí crea una matriz de objetos con una sola propiedad, no contrae todas las propiedades en un solo objeto.
joshperry
16

Esta función debe manejar matrices multidimensionales junto con múltiples elementos con el mismo nombre.

Lo he estado usando durante un par de años hasta ahora:

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

Puedes hacerlo:

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

Ver JSON .

Harini Sekar
fuente
14

One-liner (sin dependencias distintas de jQuery), utiliza el enlace de objeto fijo para la función pasada al mapmétodo.

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

¿Que hace?

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

Adecuado para aplicaciones web progresivas (uno puede admitir fácilmente tanto la acción de envío de formularios regulares como las solicitudes de ajax)

test30
fuente
12

Utilizar:

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

Salida:

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

La simplicidad es mejor aquí. He usado un simple reemplazo de cadena con una expresión regular, y hasta ahora funcionaron como un encanto. No soy un experto en expresiones regulares, pero apuesto a que incluso puedes llenar objetos muy complejos.

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

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

De alguna respuesta anterior :

$('form input, form select').toArray().reduce(function(m,e){m[e.name] = $(e).val(); return m;},{})
juanpastas
fuente
Por lo que puedo decir, la diferencia es que su solución no depende, por serializeArraylo que tiene la libertad de elegir las entradas que desee (por ejemplo, puede incluir entradas deshabilitadas), ¿verdad? Es decir, esto no está asociado a ningún formulario o al evento de envío, ¿es solo independiente por sí mismo?
dvtan
la única pequeña diferencia con la respuesta vinculada es que no se necesitan datos para crear instancias, reduce devuelve el objeto. Esto no es independiente ya que toArrayes de jQuery.
sitios
6

Encontré un problema con el código de Tobias Cohen (no tengo suficientes puntos para comentarlo directamente), que de lo contrario me funciona. Si tiene dos opciones de selección con el mismo nombre, ambas con valor = "", el código original generará "nombre": "" en lugar de "nombre": ["", ""]

Creo que esto se puede solucionar agregando "|| o [this.name] == ''" a la primera condición 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;
};
usuario1134789
fuente
6

Usando la solución de maček , la modifiqué para que funcione con la forma en que ASP.NET MVC maneja sus objetos anidados / complejos en el mismo formulario. Todo lo que tiene que hacer es modificar la pieza de validación para esto:

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

Esto coincidirá y luego correlacionará correctamente los elementos con nombres como:

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

Y

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

la forma más sencilla y precisa he encontrado para este problema era utilizar plug-in barbacoa o esta uno (que es aproximadamente 0,5 K bytes de tamaño).

También funciona con matrices multidimensionales.

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

Roey
fuente
Esto parece funcionar bien. Hay un repositorio alternativo para jquery-deparam que incluye archivos de descripción para bower y npm.
Alf Eaton
4

Hay un complemento para hacer eso para jQuery, jquery.serializeJSON . Lo he usado con éxito en algunos proyectos ahora. Funciona a las mil maravillas.

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

let formDataJSON = {};

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

    formDataJSON[key] = value;
}
Punta de piedra
fuente
3

Prefiero este enfoque porque: no tiene que iterar sobre 2 colecciones, puede obtener otras cosas que no sean "nombre" y "valor" si es necesario, y puede desinfectar sus valores antes de almacenarlos en el objeto ( si tiene valores predeterminados que no desea almacenar, por ejemplo).

$.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 así:

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

Solo probado en Firefox.

kflorence
fuente
3

Convierta cualquier cosa en un objeto (no unidad probada)

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

La salida de prueba:

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

en

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

rendirá:

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

Encontré un problema con la solución seleccionada.

Cuando se usan formularios que tienen nombres basados ​​en matrices, la función jQuery serializeArray () en realidad muere.

Tengo un marco PHP que usa nombres de campo basados ​​en matrices para permitir que el mismo formulario se coloque en la misma página varias veces en múltiples vistas. Esto puede ser útil para agregar, editar y eliminar en la misma página sin modelos de formulario en conflicto.

Como quería seralizar los formularios sin tener que sacar esta funcionalidad de base absoluta, decidí escribir mi propio 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;
            }
        });

Tenga en cuenta: esto también funciona fuera del formulario submit (), por lo que si se produce un error en el resto de su código, el formulario no se enviará si coloca un botón de enlace que dice "guardar cambios".

También tenga en cuenta que esta función nunca debe usarse para validar el formulario solo para recopilar los datos para enviarlos al lado del servidor para su validación. El uso de dicho código débil y asignado en masa causará XSS , etc.

Sammaye
fuente
3

Tuve el mismo problema últimamente y salí con este complemento .toJSON jQuery que convierte un formulario en un objeto JSON con la misma estructura. Esto también es especialmente útil para formularios generados dinámicamente donde desea permitir que su usuario agregue más campos en lugares específicos.

El punto es que es posible que desee crear un formulario para que tenga una estructura en sí misma, así que supongamos que desea crear un formulario donde el usuario inserte sus lugares favoritos en la ciudad: puede imaginar este formulario para representar un <places>...</places>elemento XML que contiene un lista de lugares que le gusta al usuario, por lo tanto, una lista de <place>...</place>elementos que contienen cada uno, por ejemplo, un <name>...</name>elemento, un <type>...</type>elemento y luego una lista de <activity>...</activity>elementos para representar las actividades que puede realizar en dicho lugar. Entonces su estructura XML sería así:

<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é bueno sería tener un objeto JSON fuera de este que representaría esta estructura exacta para que pueda:

  • Almacene este objeto como está en cualquier CouchDB base de datos tipo
  • Léalo desde el lado del servidor $ _POST [] y recupere una matriz correctamente anidada que luego puede manipular semánticamente
  • Utilice algún script del lado del servidor para convertirlo en un archivo XML bien formado (incluso si no conoce su estructura exacta a priori)
  • Solo de alguna manera, úselo como está en cualquier script de servidor similar a Node.js

Bien, ahora debemos pensar cómo un formulario puede representar un archivo XML.

Por supuesto, la <form>etiqueta es la root, pero luego tenemos que<place> elemento que es un contenedor y no un elemento de datos en sí mismo, por lo que no podemos usar una etiqueta de entrada para ello.

¡Aquí es donde la <fieldset>etiqueta es útil! Usaremos <fieldset>etiquetas para representar todos los elementos del contenedor en nuestra representación de formulario / XML y así llegar a un 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 puede ver en este formulario, estamos rompiendo la regla de los nombres únicos, pero esto está bien porque se convertirán en una matriz de elementos, por lo que solo se referenciará por su índice dentro de la matriz.

En este punto, puede ver cómo no hay name="array[]"un nombre similar dentro del formulario y todo es bonito, simple y semántico.

Ahora queremos que este formulario se convierta en un objeto JSON que se verá así:

{'places':{

    'place':[

        {

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

            'activity':[

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

            ]

        },

        {...},

        {...}

    ]

}}

Para hacer esto, he desarrollado este complemento jQuery aquí que alguien ayudó a optimizar en este hilo de Revisión de Código y se ve así:

$.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;
};

También hice esta publicación de blog para explicar esto más.

Esto convierte todo en un formulario a JSON (incluso radio y casillas de verificación) y todo lo que le queda por hacer es llamar

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

Sé que hay muchas maneras de convertir formularios en objetos JSON y de seguro .serialize()y .serializeArray()funcionan muy bien en la mayoría de los casos, y en su mayoría están destinados a ser utilizados, pero creo que esta idea es escribir un formulario como una estructura XML con nombres significativos y convertirlo en un objeto JSON bien formado Vale la pena intentarlo con un , también el hecho de que puede agregar etiquetas de entrada del mismo nombre sin preocuparse es muy útil si necesita recuperar datos de formularios generados dinámicamente.

¡Espero que esto ayude a alguien!

Onheiron
fuente
¿que estás tratando de hacer?
Onheiron
3

Codifiqué un formulario en un objeto JavaScript multidimensional para usarlo en producción. El resultado es https://github.com/serbanghita/formToObject.js .

serbanghita
fuente
Utilicé una variación de esto para una implementación muy específica, ¡muchas gracias!
Chris Baker
Gracias por dejarme saber que es útil. Tengo algunas características entrantes para rodar y esto me motiva.
Șerban Ghiță
3

Otra respuesta

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

[ACTUALIZACIÓN 2020]

Con un simple oneliner en vainilla js:

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

Me gusta la versión de Samuel, pero creo que tiene un pequeño error. Normalmente JSON se envía como

{"coreSKU": "PCGUYJS", "name_de": "lo que sea", ...

NO como

[{"coreSKU": "PCGUYJS"}, {"name_de": "lo que sea"}, ...

entonces la función IMO debería leer:

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

y envolverlo en una matriz de datos (como comúnmente se espera), y finalmente enviarlo como astring App.stringify ({data: App.toJson ('#cropform: input')})

Para ver el stringify, vea la pregunta 3593046 para la versión ajustada , y json2.js para la versión cubierta por todas las eventualidades. Eso debería cubrirlo todo :)

revs Frank Nocke
fuente
Gracias ... esto hace (como mencionaste) una pequeña pero muy importante diferencia.
Adarsha
2

Para una solución rápida y moderna, use JSONify complemento jQuery. El siguiente ejemplo se toma textualmente del archivo README de GitHub. Todo el crédito a Kushal Pandya, autor del complemento.

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>

Corriendo:

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

Produce:

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

Si desea hacer una POST jQuery con este objeto JSON:

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