Convertir des données de formulaire en objet JavaScript avec jQuery

1625

Comment convertir tous les éléments de mon formulaire en objet JavaScript?

J'aimerais avoir un moyen de construire automatiquement un objet JavaScript à partir de mon formulaire, sans avoir à parcourir chaque élément. Je ne veux pas de chaîne, telle que renvoyée par $('#formid').serialize();, ni la carte retournée par$('#formid').serializeArray();

Yisroel
la source
16
car la première renvoie une chaîne, exactement comme ce que vous obtiendriez si vous soumettiez le formulaire avec une méthode GET, et la seconde vous donne un tableau d'objets, chacun avec une paire de valeurs de nom. Je veux que si j'ai un champ nommé "email" j'obtiens un objet qui me permettra de récupérer cette valeur avec obj.email. Avec serializeArray (), je devrais faire quelque chose comme obj [indexOfElement] .value
Yisroel
2
@James - La réponse acceptée à l'aide de la bibliothèque JSON-js de D. Crockford. Voici un exemple: github.com/tleese22/google-app-engine-jappstart/blob/master/src/…
Taylor Leese
4
@ Taylor Oui, je dirais que la bonne réponse utilise la fonction lib et Tobias de Crockford comme ceci: JSON.stringify ($ ('myForm'). SerializeObject ())
James McCormack
5
@Jonz - Il existe d'autres raisons que la soumission / transmission pour utiliser un élément de formulaire. Si vous faites de gros efforts avec les valeurs de formulaire dans JavaScript (par exemple, l'application à page unique), il est très pratique de les avoir dans un format d'objet pour accéder et manipuler. De plus, les chaînes de requête HTTP Post et Get ne sont pas les seuls formats pour déplacer des données.
Patrick M
3
Un bon js que j'ai rencontré >> github.com/marioizquierdo/jquery.serializeJSON
Bongs

Réponses:

1657

serializeArrayfait déjà exactement cela. Vous avez juste besoin de masser les données dans votre format requis:

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

Attention aux champs cachés qui ont le même nom que les entrées réelles car ils seront écrasés.

Tobias Cohen
la source
4
comme le dit tvanfosson, pourquoi répéter deux fois la collection?
Yisroel
69
Vous voulez dire "pourquoi utiliser serializeArray pour obtenir les données en premier lieu?" Étant donné que serializeArray est déjà écrit, il est testé à l'unité dans plusieurs navigateurs et pourrait théoriquement être amélioré dans les versions ultérieures de jQuery. Moins vous écrivez de code qui doit accéder directement à des éléments incohérents comme les éléments DOM, plus votre code sera stable.
Tobias Cohen
56
Soyez averti, serializeArray () n'inclura pas les éléments désactivés. Je désactive souvent les éléments d'entrée qui sont synchronisés avec d'autres éléments de la page, mais je veux toujours qu'ils soient inclus dans mon objet sérialisé. Il vaut mieux utiliser quelque chose comme $.map( $("#container :input"), function(n, i) { /* n.name and $(n).val() */ } );si vous devez inclure des éléments désactivés.
Samuel Meacham
22
@TobiasCohen Il ne gère pas les foo[bar]entrées de type comme prévu, sans parler de la plupart des autres variétés de noms d'entrée. Après avoir été très frustré par des solutions superficielles à ce problème, j'ai fini par écrire mon propre plugin jQuery - détails dans la réponse que j'ai fournie à cette question.
maček
6
@macek Je sais que cela date de quelques mois, mais depuis quand les tableaux utilisent-ils des index non numériques? Personne ne devrait nommer une entrée foo [bar] et espérer la traiter comme un tableau. Êtes-vous prêt à confondre les tableaux et les hachages? Oui, [] est généralement considéré comme un accesseur, mais pas uniquement pour les tableaux. Également dire que c'est du HTML valide mais pas dans la spécification HTML est une contradiction. Oui, le navigateur ne s'étouffe pas, mais peu de serveurs Web sauront comment désérialiser cela comme ils le feraient pour un tableau. Pourquoi? Parce que ce n'est pas dans la spécification HTML. Par conséquent, il est en effet invalide.
kroehre
446

Convertir des formulaires en JSON comme un boss


La source actuelle est sur GitHub et Bower .

$ bower install jquery-serialize-object


Le code suivant est désormais obsolète .

Le code suivant peut fonctionner avec toutes sortes de noms d'entrée; et gérez-les comme vous vous en doutez.

Par exemple:

<!-- 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"
          }
        }
      }
    }
  }
}

Usage

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

La sorcellerie (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
la source
17
Donc ça marche plutôt bien. Mais il est mal nommé: il ne renvoie pas JSON, comme son nom l'indique. Au lieu de cela, il renvoie un objet littéral. De plus, il est important de vérifier hasOwnProperty, sinon vos tableaux ont tout ce qui est attaché à leur prototype, comme: {nombres: ["1", "3", indexOf: function () {...}]}
frontendbeauty
5
@frontendbeauty en fait, toJSON est exactement ce que la spécification dit qu'il devrait être appelé: developer.mozilla.org/en/JSON#toJSON()_method un malencontreux impromptu.
Ryan Florence
4
@Marek, j'ai fait un test ici sur jsfiddle . L'astuce consiste à nommer correctement votre sélection. <select name="foo" multiple="multiple">ne fonctionnera dans aucun scénario. Cependant, si vous utilisez [], comme dans <select name="bar[]" multiple="multiple">, cela fonctionnera très bien :)
maček
3
Je voulais souligner que github.com/serbanghita/formToObject est une méthode JavaScript similaire (pas de bibliothèques tierces nécessaires) qui fait le même travail. Prend en charge les «multiples», ignore les éléments «désactivés».
Șerban Ghiță
8
Cette solution devrait être au top car elle traite du problème des clés imbriquées en tant que noms d'éléments de formulaire.
SquareCat
283

Quel est le problème avec:

var data = {};
$(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); 
mkschreder
la source
2
@LayZee - si rien n'est sélectionné, pourquoi le voudriez-vous sur votre back-end? si vous devez sélectionner une option, validez l'entrée avant la sérialisation.
Dementic
21
si vous ne retournez rien, pourquoi ne pas simplement utiliser à la .eachplace de .map??
azerafati
10
C'est si simple parce que c'est super naïf ... ne gère pas les cases à cocher du même nom. Chaque fois, il remplacera l'élément vérifié avant lui. Vous aurez certainement besoin d'une détection de type d'entrée pour vous assurer qu'il est correctement sérialisé.
Joshua F. Rountree
5
Si vous voulez une solution jQuery pure, vous pouvez utiliservar 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;}, {})
sites
103

Une version fixe de la solution de Tobias Cohen. Celui-ci gère correctement les valeurs de falsification comme 0et ''.

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

Et une version CoffeeScript pour votre commodité de codage:

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
la source
Dans le coffeescript, ce premier si le bloc peut être raccourci àvalue = @value ? ''
nzifnab
Et si vous essayez de sérialiser des formulaires de type Rails, vous souhaiterez peut-être supprimer la racine de l'élément des clés générées. Pour cela, j'ai ajouté un nouveau param keyMapet la ligne suivante: key = if keyMap? then keyMap(@name) else @name. Maintenant, vous pouvez passer la fonction de cartographie comme (name) -> name.match(/\[([^\]]+)]/)[1]. Et puis il faudrait tout changer @nameaprès key, bien sûr
Damir Zekić
@ DamirZekić, si votre élément racine était post, vous pourriez simplement le faire $('form').serializeObject().post. Pas besoin de cartographie sophistiquée.
maček
@ DamirZekić Que fait cette ligne? if (!objectData[this.name].push)??
kittu
@kittu Il vérifie si objectData[this.name]a la méthode push (en gros, est-ce un tableau). S'il s'agit d'un tableau, il pousse la valeur, s'il ne s'agit pas d'un tableau, il le convertit en tableau afin que plusieurs valeurs avec la même clé soient combinées en un tableau.
Daniel X Moore
63

J'aime utiliser Array.prototype.reduceparce que c'est une doublure, et elle ne repose pas sur Underscore.js ou similaire:

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

Ceci est similaire à la réponse utilisée Array.prototype.map, mais vous n'avez pas besoin d'encombrer votre portée avec une variable d'objet supplémentaire. Un guichet unique.

REMARQUE IMPORTANTE : les formulaires avec des entrées qui ont des nameattributs en double sont du HTML valide et sont en fait une approche courante. L'utilisation de l'une des réponses de ce fil sera inappropriée dans ce cas (car les clés d'objet doivent être uniques).

Ethan Brown
la source
C'est assez élégant. Mais il convient de noter que ce array.prototype.reduce()n'est pas disponible dans IE8 car il fait partie de la spécification ECMAScript 5. Donc, si vous avez besoin du support IE8, vous voudrez utiliser un polyfill ou une autre solution.
gfullam
3
C'est vrai, mais il est assez facile de polyfill. De plus, les maux de tête IE8 sont presque terminés, grâce à l'excellent travail de Microsoft avec IE11 Enterprise Mode - je ne réponds plus aux utilisateurs individuels avec IE8, mais lorsqu'une organisation de 10000 employés qui utilisent tous IE8 arrive ... c'est différent. Heureusement, Microsoft travaille dur sur ce problème.
Ethan Brown
1
Eh bien, vos informaticiens devraient se pencher sur le mode IE11 Enterprise - il fournit un navigateur moderne ET un moyen d'exécuter des applications compatibles IE8. Déménagement intelligent de la part de Microsoft: howtogeek.com/184634/…
Ethan Brown
30

Toutes ces réponses me semblaient tellement exagérées. Il y a quelque chose à dire pour plus de simplicité. Tant que toutes vos entrées de formulaire ont l'attribut name défini, cela devrait fonctionner uniquement avec 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
la source
4
Ne gère pas les données de formulaire imbriquées, c'est pourquoi les réponses deviennent plus compliquées.
frontendbeauty
A très bien fonctionné pour moi. Juste besoin de stockage de paires clé-valeur; le filtrage, puis l'enregistrement dans localStorage ou le cookie a fonctionné tout simplement.
Greg Pettit
23

Il n'y a vraiment aucun moyen de le faire sans examiner chacun des éléments. Ce que vous voulez vraiment savoir, c'est "quelqu'un d'autre a-t-il déjà écrit une méthode qui convertit un formulaire en objet JSON?" Quelque chose comme ce qui suit devrait fonctionner - notez qu'il ne vous donnera que les éléments de formulaire qui seraient retournés via un POST (doit avoir un nom). Ceci n'est pas testé .

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
la source
c'est vrai, j'adorerais un plugin qui ferait ça pour moi. la limitation d'avoir un nom n'est pas très grave. cela va-t-il extraire tous les champs d'un formulaire, y compris les zones de texte et les sélections?
Yisroel
Je ne sais pas si vous voulez prendre en compte [désactivé] mais je ne pense pas que cela devrait être envoyé / récupéré.
meder omuraliev
pourrait être plus simple d'utiliser serializeArray () pour obtenir la carte, puis utilisez un code similaire à celui ci-dessus pour le convertir en un objet JSON normal, de cette façon, je ne traite pas le formulaire lui
Yisroel
2
L'utilisation de serializedArray fonctionnerait, mais essentiellement vous itéreriez deux fois sur la collection - une fois pour produire le tableau, puis sur le tableau. Je n'en vois pas la nécessité.
tvanfosson
J'ajusterai le sélecteur pour activé / désactivé.
tvanfosson
23

Si vous utilisez Underscore.js, vous pouvez utiliser le relativement concis:

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

J'ai vérifié qu'il y a un problème avec toutes les autres réponses, que si le nom d'entrée est comme un tableau, comme name[key], alors il doit être généré comme ceci:

name:{ key : value }


Par exemple: Si vous avez un formulaire HTML similaire à celui ci-dessous:

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

Mais il doit être généré comme le JSON ci-dessous et ne devient pas un objet comme le suivant avec toutes les autres réponses. Donc, si quelqu'un veut apporter quelque chose comme le JSON suivant, essayez le code JS ci-dessous.

{
    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
la source
Cette réponse couvre le cas mentionné, mais elle ne couvre pas les cas comme la case à cocher [] ou même l'un [l'autre] [un autre_one]
Leonardo Beal
1
@LeonardoBeal je répare mes ans .. vérifiez cela maintenant ..!
Bhavik Hirani, le
Je suis le downvoter et j'ai downvoté car evalne doit pas être utilisé de cette façon car eval, lorsqu'il est utilisé de cette manière, favorise des pratiques terriblement peu fiables, buggy, peu performantes et potentiellement non sécurisées.
Jack Giffin
2
Je ne peux pas convenir que c'est une bonne réponse. Et s'il vous plaît, lorsque vous écrivez des réponses, expliquez votre code de lui-même ou expliquez-le. this.c = function(k,v){ eval("c = typeof "+k+";"); if(c == 'undefined') _t.b(k,v);}est court et non explicatif. Un développeur avec moins d'expérience copiera simplement ceci sans comprendre pourquoi et comment cela fonctionne.
iRaS
2
@JackGiffin Consultez mon nouveau code maintenant parce que je l'ai supprimé eval()de mon code.
Bhavik Hirani
17

Ok, je sais que cela a déjà une réponse très appréciée, mais une autre question similaire a été posée récemment, et j'ai également été dirigée vers cette question. Je voudrais également proposer ma solution, car elle offre un avantage sur la solution acceptée: vous pouvez inclure des éléments de formulaire désactivés (ce qui est parfois important, selon le fonctionnement de votre interface utilisateur)

Voici ma réponse de l' autre question SO :

Initialement, nous utilisions la serializeArray()méthode de jQuery , mais cela n'inclut pas les éléments de formulaire qui sont désactivés. Nous désactiverons souvent les éléments de formulaire qui sont "synchronisés" avec d'autres sources sur la page, mais nous devons toujours inclure les données dans notre objet sérialisé. Il en serializeArray()va de même. Nous avons utilisé le :inputsélecteur pour obtenir tous les éléments d'entrée (activés et désactivés) dans un conteneur donné, puis $.map()pour créer notre objet.

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

Notez que pour que cela fonctionne, chacune de vos entrées aura besoin d'un nameattribut, qui sera le nom de la propriété de l'objet résultant.

C'est en fait légèrement modifié de ce que nous avons utilisé. Nous avions besoin de créer un objet qui était structuré en tant que .NET IDictionary, nous l'avons donc utilisé: (je le fournis ici au cas où cela serait utile)

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

J'aime ces deux solutions, car ce sont des utilisations simples de la $.map()fonction, et vous avez un contrôle complet sur votre sélecteur (donc, quels éléments vous finissez par inclure dans votre objet résultant). De plus, aucun plugin supplémentaire n'est requis. Plaine ancienne jQuery.

Samuel Meacham
la source
6
J'ai essayé cela dans un projet, en utilisant mapcomme cela crée un tableau d'objets avec une seule propriété, il ne réduit pas toutes les propriétés en un seul objet.
joshperry
16

Cette fonction doit gérer des tableaux multidimensionnels ainsi que plusieurs éléments portant le même nom.

Je l'utilise depuis quelques années:

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
la source
15

Tu peux le faire:

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

Voir JSON .

Harini Sekar
la source
14

One-liner (pas de dépendances autres que jQuery), utilise une liaison d'objet fixe pour la fonction passée à la mapméthode.

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

Ce qu'il fait?

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

adapté aux applications Web progressives (on peut facilement prendre en charge à la fois l'action de soumission de formulaire régulière ainsi que les demandes ajax)

test30
la source
12

Utilisation:

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

Production:

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

La simplicité est meilleure ici. J'ai utilisé un simple remplacement de chaîne par une expression régulière, et ils ont fonctionné comme un charme jusqu'à présent. Je ne suis pas un expert en expression régulière, mais je parie que vous pouvez même remplir des objets très complexes.

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

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

D'après une réponse plus ancienne :

$('form input, form select').toArray().reduce(function(m,e){m[e.name] = $(e).val(); return m;},{})
juanpastas
la source
D'après ce que je peux dire, la différence est que votre solution ne dépend pas serializeArray, vous avez donc la liberté de choisir les entrées que vous voulez (par exemple, vous pouvez inclure des entrées désactivées), non? C'est à dire que ce n'est pas couplé à une forme ou à l'événement de soumission, c'est juste indépendant par lui-même?
dvtan
la seule petite différence avec la réponse liée est qu'il n'y a pas de données nécessaires pour instancier, reducerenvoie l'objet. Ce n'est pas indépendant car toArrayvient de jQuery.
sites
6

J'ai trouvé un problème avec le code de Tobias Cohen (je n'ai pas assez de points pour le commenter directement), qui sinon fonctionne pour moi. Si vous avez deux options de sélection avec le même nom, toutes deux avec value = "", le code d'origine produira "name": "" au lieu de "name": ["", ""]

Je pense que cela peut être corrigé en ajoutant "|| o [this.name] == ''" à la première condition 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
la source
6

En utilisant la solution de maček , je l'ai modifiée pour fonctionner avec la façon dont ASP.NET MVC gère leurs objets imbriqués / complexes sur le même formulaire. Il vous suffit de modifier la pièce de validation en ceci:

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

Cela correspondra, puis mappera correctement les éléments avec des noms comme:

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

Et

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

le moyen le plus simple et le plus précis que j'ai trouvé pour ce problème était d'utiliser le plugin bbq ou celui- ci (qui fait environ 0,5 K octets).

il fonctionne également avec des tableaux multidimensionnels.

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

Roey
la source
Cela semble bien fonctionner. Il existe un autre référentiel pour jquery-deparam qui inclut des fichiers de description pour bower et npm.
Alf Eaton
4

Il existe un plugin pour faire exactement cela pour jQuery, jquery.serializeJSON . Je l'ai utilisé avec succès sur quelques projets maintenant. Il fonctionne comme un charme.

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

let formDataJSON = {};

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

    formDataJSON[key] = value;
}
Stonetip
la source
3

Je préfère cette approche car: vous n'avez pas à parcourir plus de 2 collections, vous pouvez obtenir autre chose que "nom" et "valeur" si vous en avez besoin, et vous pouvez nettoyer vos valeurs avant de les stocker dans l'objet ( si vous avez des valeurs par défaut que vous ne souhaitez pas stocker, par exemple).

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

Utilisez comme ça:

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

Uniquement testé dans Firefox.

kflorence
la source
3

Transformez n'importe quoi en objet (non testé à l'unité)

<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 sortie de test:

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

sur

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

donnera:

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

J'ai trouvé un problème avec la solution sélectionnée.

Lorsque vous utilisez des formulaires qui ont des noms basés sur des tableaux, la fonction jQuery serializeArray () meurt réellement.

J'ai un framework PHP qui utilise des noms de champs basés sur des tableaux pour permettre au même formulaire d'être mis sur la même page plusieurs fois dans plusieurs vues. Cela peut être pratique pour mettre à la fois ajouter, modifier et supprimer sur la même page sans modèles de formulaire en conflit.

Comme je voulais séraliser les formulaires sans avoir à retirer cette fonctionnalité de base absolue, j'ai décidé d'écrire mon propre 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;
            }
        });

Veuillez noter: cela fonctionne également en dehors du formulaire submit (), donc si une erreur se produit dans le reste de votre code, le formulaire ne sera pas soumis si vous placez sur un bouton de lien disant "enregistrer les modifications".

Notez également que cette fonction ne doit jamais être utilisée pour valider le formulaire uniquement pour collecter les données à envoyer côté serveur pour validation. L'utilisation de ce code faible et assigné en masse provoquera XSS , etc.

Sammaye
la source
3

J'ai eu le même problème récemment et je suis sorti avec ce plugin .toJSON jQuery qui convertit un formulaire en un objet JSON avec la même structure. Cela est également particulièrement utile pour les formulaires générés dynamiquement où vous souhaitez laisser votre utilisateur ajouter plus de champs à des endroits spécifiques.

Le fait est que vous voudrez peut-être réellement créer un formulaire afin qu'il ait une structure elle-même, alors disons que vous voulez créer un formulaire où l'utilisateur insère ses endroits préférés en ville: vous pouvez imaginer ce formulaire pour représenter un <places>...</places>élément XML contenant un liste de lieux l'utilisateur aime ainsi une liste d' <place>...</place>éléments contenant chacun par exemple un <name>...</name>élément, un <type>...</type>élément puis une liste d' <activity>...</activity>éléments pour représenter les activités que vous pouvez effectuer dans un tel endroit. Donc, votre structure XML serait comme ceci:

<places>

    <place>

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

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

    </place>

    <place>...</place>

    <place>...</place>

</places>

À quel point ce serait cool d'avoir un objet JSON à partir de cela qui représenterait cette structure exacte afin que vous puissiez soit:

  • Stockez cet objet tel qu'il est dans n'importe quelle base de données de type CouchDB
  • Lisez-le depuis votre serveur $ _POST [] et récupérez un tableau correctement imbriqué que vous pouvez ensuite manipuler sémantiquement
  • Utilisez un script côté serveur pour le convertir en un fichier XML bien formé (même si vous ne connaissez pas sa structure exacte a priori)
  • Il suffit de l'utiliser en quelque sorte tel qu'il est dans n'importe quel script de serveur de type Node.js

OK, nous devons donc maintenant réfléchir à la manière dont un formulaire peut représenter un fichier XML.

Bien sûr, la <form>balise est le root, mais nous avons cet <place>élément qui est un conteneur et non un élément de données lui-même, nous ne pouvons donc pas utiliser de balise d'entrée pour cela.

Voici où la <fieldset>balise est utile! Nous utiliserons des <fieldset>balises pour représenter tous les éléments de conteneur dans notre représentation de formulaire / XML et ainsi obtenir un résultat comme celui-ci:

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

Comme vous pouvez le voir dans ce formulaire, nous enfreignons la règle des noms uniques, mais c'est OK car ils seront convertis en un tableau d'éléments donc ils ne seront référencés que par leur index à l'intérieur du tableau.

À ce stade, vous pouvez voir comment il n'y a pas de name="array[]"nom similaire dans le formulaire et tout est joli, simple et sémantique.

Maintenant, nous voulons que ce formulaire soit converti en un objet JSON qui ressemblera à ceci:

{'places':{

    'place':[

        {

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

            'activity':[

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

            ]

        },

        {...},

        {...}

    ]

}}

Pour ce faire, j'ai développé ce plugin jQuery ici que quelqu'un a aidé à optimiser dans ce fil de révision de code et ressemble à ceci:

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

J'ai aussi fait ce blog pour expliquer cela davantage.

Cela convertit tout dans un formulaire en JSON (même radio et cases à cocher) et tout ce qu'il vous reste à faire est d'appeler

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

Je sais qu'il existe de nombreuses façons de convertir des formulaires en objets JSON et bien sûr .serialize()et .serializeArray()très bien dans la plupart des cas et sont principalement destinés à être utilisés, mais je pense que toute cette idée d' écrire un formulaire comme une structure XML avec des noms significatifs et de le convertir en un Un objet JSON bien formé vaut la peine d'être essayé, le fait que vous puissiez ajouter des balises d'entrée du même nom sans vous inquiéter est très utile si vous devez récupérer des données de formulaires générées dynamiquement.

J'espère que ça aidera quelqu'un!

Onheiron
la source
Qu'essayez-vous de faire?
Onheiron
3

J'ai codé moi-même un formulaire en un objet JavaScript multidimensionnel pour l'utiliser en production. Le résultat est https://github.com/serbanghita/formToObject.js .

serbanghita
la source
J'en ai utilisé une variante pour une implémentation très spécifique, merci beaucoup!
Chris Baker
Merci de m'avoir fait savoir que c'est utile. J'ai quelques fonctionnalités entrantes à rouler et cela me motive.
Șerban Ghiță
3

Une autre réponse

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
la source
3

[MISE À JOUR 2020]

Avec un simple oneliner en vanille js:

Object.fromEntries(new FormData(form))
aret
la source
2

J'aime la version samuels, mais je crois qu'elle a une petite erreur. Normalement, JSON est envoyé en tant que

{"coreSKU": "PCGUYJS", "name_de": "que ce soit", ...

Pas aussi

[{"coreSKU": "PCGUYJS"}, {"name_de": "que ce soit"}, ...

donc la fonction IMO devrait se lire:

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

et de l'envelopper dans un tableau de données (comme cela est généralement prévu aussi), et enfin de l'envoyer sous astring App.stringify ({data: App.toJson ('#cropform: input')})

Pour le regard stratifié à la question 3593046 pour la version allégée, à json2.js pour la version couverte par toutes les éventualités. Cela devrait tout couvrir :)

Frank Nocke
la source
Merci .. cela fait (comme vous l'avez mentionné) une différence minuscule mais très importante.
Adarsha
2

Pour une solution rapide et moderne, utilisez JSONify plugin jQuery. L'exemple ci-dessous est extrait textuellement du fichier LISEZMOI GitHub. Tout le crédit à Kushal Pandya, auteur du plugin.

Donné:

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

Fonctionnement:

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

Produit:

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

Si vous voulez faire un jQuery POST avec cet objet JSON:

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