Converti i dati del modulo in oggetto JavaScript con jQuery


1624

Come faccio a convertire tutti gli elementi del mio modulo in un oggetto JavaScript?

Mi piacerebbe avere un modo per costruire automaticamente un oggetto JavaScript dal mio modulo, senza dover passare su ogni elemento. Non voglio una stringa, come restituita da $('#formid').serialize();, né voglio che la mappa venga restituita da$('#formid').serializeArray();


16
perché il primo restituisce una stringa, esattamente come quello che otterresti se inviassi il modulo con un metodo GET, e il secondo ti dà una matrice di oggetti, ognuno con una coppia nome-valore. Voglio che se ho un campo chiamato "e-mail" ottengo un oggetto che mi permetterà di recuperare quel valore con obj.email. Con serializeArray (), dovrei fare qualcosa come obj [indexOfElement] .value
Yisroel,

2
@James - La risposta accettata utilizzando la libreria JSON-js di D. Crockford. Ecco un esempio: github.com/tleese22/google-app-engine-jappstart/blob/master/src/…
Taylor Leese

4
@Taylor Sì, direi che la risposta corretta usa la lib di Crockford e la funzione di Tobias in questo modo: JSON.stringify ($ ('myForm'). SerializeObject ())
James McCormack,

5
@Jonz - Esistono altri motivi oltre all'invio / alla trasmissione per l'utilizzo di un elemento modulo. Se stai eseguendo operazioni pesanti con i valori del modulo in JavaScript (ad es. App a pagina singola), è molto utile averli in un formato oggetto per l'accesso e la manipolazione. Inoltre, HTTP Post e Get stringhe di query non sono gli unici formati per lo spostamento dei dati.
Patrick M,

3
Un buon js mi sono imbattuto >> github.com/marioizquierdo/jquery.serializeJSON
Bongs

Risposte:


1657

serializeArraylo fa già esattamente. Hai solo bisogno di massaggiare i dati nel formato richiesto:

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

Fai attenzione ai campi nascosti che hanno lo stesso nome degli input reali in quanto verranno sovrascritti.


4
come dice tvanfosson, perché scorrere due volte la raccolta?
Yisroel,

69
Intendi "perché utilizzare serializeArray per ottenere i dati in primo luogo?" Poiché serializeArray è già stato scritto, è testato in unità in più browser e teoricamente potrebbe essere migliorato nelle versioni successive di jQuery. Meno codice scrivi e devi accedere direttamente a elementi incoerenti come gli elementi DOM, più stabile sarà il tuo codice.
Tobias Cohen,

56
Attenzione, serializeArray () non includerà elementi disabilitati. Spesso disabilito gli elementi di input sincronizzati con altri elementi della pagina, ma li voglio ancora inclusi nel mio oggetto serializzato. Stai meglio usando qualcosa come $.map( $("#container :input"), function(n, i) { /* n.name and $(n).val() */ } );se dovessi includere elementi disabilitati.
Samuel Meacham,

22
@TobiasCohen Non gestisce foo[bar]input di tipo come previsto, per non parlare della maggior parte delle altre varietà di nomi di input. Dopo essere stato molto frustrato dalle soluzioni superficiali a questo problema, ho finito per scrivere il mio plugin jQuery - i dettagli nella risposta che ho fornito a questa domanda.
maček,

6
@Macek So che ha alcuni mesi, ma da quando gli array usano indici non numerici? Nessuno dovrebbe nominare un input foo [bar] e sperare di trattarlo come un array. Stai confondendo array e hash? Sì, [] è comunemente inteso come un accessorio, ma non solo per gli array. Anche dire che è HTML valido ma non nelle specifiche HTML è una contraddizione. Sì, il browser potrebbe non soffocarlo, ma non molti server web sapranno come deserializzare quello come se fossero un array. Perché? Perché non è nelle specifiche HTML. Pertanto, è effettivamente non valido.
kroehre,

446

Converti i moduli in JSON come un boss


La fonte attuale è su GitHub e Bower .

$ bower installa jquery-serialize-object


Il seguente codice è ora obsoleto .

Il seguente codice può funzionare con tutti i tipi di nomi di input; e gestirli come ti aspetteresti.

Per esempio:

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

The Stregoneria (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);

17
Quindi, funziona abbastanza bene. Ma è erroneamente chiamato: non restituisce JSON, come suggerisce il nome. Al contrario, restituisce un oggetto letterale. Inoltre, è importante controllare hasOwnProperty, altrimenti le tue matrici hanno qualcosa che è attaccato al loro prototipo, come: {numeri: ["1", "3", indexOf: function () {...}]}
frontendbeauty

5
@frontendbeauty in realtà, toJSON è esattamente ciò che la specifica dice che dovrebbe essere chiamato: developer.mozilla.org/en/JSON#toJSON()_method uno sfortunato termine improprio.
Ryan Florence,

4
@Marek, ho fatto un test qui su jsfiddle . Il trucco è nominare correttamente la tua selezione. <select name="foo" multiple="multiple">non funzionerà in nessun caso. Tuttavia, se usi [], come in <select name="bar[]" multiple="multiple">, funzionerà bene :)
maček

3
Volevo sottolineare che github.com/serbanghita/formToObject è un metodo JavaScript simile (non sono necessarie librerie di terze parti) che fa lo stesso lavoro. Supporta "multipli", ignora gli elementi "disabilitati".
banerban Ghiță,

8
Questa soluzione dovrebbe essere in primo piano poiché affronta il problema delle chiavi nidificate come nomi di elementi del modulo.
SquareCat,

283

Cosa c'è di sbagliato in:

var data = {};
$(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); 

2
@LayZee: se non è selezionato nulla, perché lo vorresti sul tuo back-end? se è necessario selezionare un'opzione, convalidare l'input prima di serializzare.
Demenza

21
se non stai restituendo nulla, perché non usi semplicemente .eachinvece di .map??
Azerafati,

10
È così semplice perché è super ingenuo ... non gestisce le caselle di controllo con lo stesso nome. Ogni volta sovrascriverà l'elemento selezionato prima di esso. Avrai sicuramente bisogno del rilevamento del tipo di input per assicurarti che sia correttamente serializzato.
Joshua F. Rountree,

5
Se vuoi una soluzione jQuery pura, puoi usarevar 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;}, {})
siti

103

Una versione fissa della soluzione di Tobias Cohen. Questo gestisce correttamente i valori falsi come 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 una versione di CoffeeScript per la tua comodità di codifica:

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

Nel coffeescript, quel primo se il blocco può essere abbreviato invalue = @value ? ''
nzifnab,

E se stai cercando di serializzare moduli simili a Rails, potresti voler rimuovere l'elemento root dalle chiavi generate. A tal fine, ho aggiunto un nuovo parametro keyMape la seguente riga: key = if keyMap? then keyMap(@name) else @name. Ora puoi passare la funzione di mappatura come (name) -> name.match(/\[([^\]]+)]/)[1]. E poi uno avrebbe bisogno di cambiare tutti i successivi @namea key, naturalmente
Damir Zekic

@ DamirZekić, se il tuo elemento radice fosse post, potresti semplicemente farlo $('form').serializeObject().post. Non c'è bisogno di mappature fantasiose.
maček,

@ DamirZekić Cosa sta facendo questa linea? if (!objectData[this.name].push)??
Kittu,

@kittu Sta verificando se objectData[this.name]ha il metodo push (approssimativamente, è un array). Se è un array, invia il valore, se non è un array, lo converte in un array in modo che più valori con la stessa chiave vengano combinati in un array.
Daniel X Moore,

63

Mi piace usare Array.prototype.reduceperché è una linea, e non si basa su Underscore.js o simili:

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

Questo è simile alla risposta usando Array.prototype.map, ma non è necessario ingombrare l'ambito con una variabile oggetto aggiuntiva. Shopping unico.

NOTA IMPORTANTE : i moduli con input che hanno nameattributi duplicati sono HTML validi ed è in realtà un approccio comune. L'uso di una qualsiasi delle risposte in questo thread sarà inappropriato in quel caso (poiché le chiavi dell'oggetto devono essere univoche).


Questo è abbastanza elegante. Ma vale la pena notare che array.prototype.reduce()non è disponibile in IE8 in quanto fa parte delle specifiche ECMAScript 5. Quindi, se hai bisogno del supporto IE8, ti consigliamo di utilizzare un polyfill o un'altra soluzione del tutto.
gfullam,

3
Vero, ma è abbastanza facile da riempire con un polifilo. Inoltre, il mal di testa di IE8 è quasi finito, grazie all'ottimo lavoro di Microsoft con IE11 Enterprise Mode: non mi rivolgo più ai singoli utenti con IE8, ma quando arriva un'organizzazione con 10.000 dipendenti che usano tutti IE8 ... è diverso. Fortunatamente, Microsoft sta lavorando duramente a questo problema.
Ethan Brown,

1
Bene, dovresti fare in modo che i tuoi esperti IT guardino alla modalità Enterprise di IE11: fornisce un browser moderno E un modo per eseguire app compatibili con IE8. Mossa intelligente da parte di Microsoft: howtogeek.com/184634/…
Ethan Brown,

30

Tutte queste risposte mi sono sembrate così esagerate. C'è qualcosa da dire per semplicità. Finché tutti gli input del modulo hanno l'attributo name impostato, questo dovrebbe funzionare 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?)
});

4
Tuttavia, non gestisce i dati dei moduli nidificati, ecco perché le risposte diventano più complicate.
frontendbeauty

Ha funzionato alla grande per me. Memoria necessaria solo per coppie chiave-valore; stringendo e poi salvando in localStorage o cookie ha funzionato semplicemente dandy.
Greg Pettit,

23

Non c'è davvero modo di farlo senza esaminare ciascuno degli elementi. Quello che vuoi veramente sapere è "qualcuno ha già scritto un metodo che converte un modulo in un oggetto JSON?" Qualcosa di simile al seguente dovrebbe funzionare - nota che ti darà solo gli elementi del modulo che verrebbero restituiti tramite un POST (deve avere un nome). Questo non è testato .

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

vero, mi piacerebbe un plugin che farebbe questo per me. la limitazione di avere un nome non è un grosso problema. questo tirerà tutti i campi in un modulo, compresi textareas e seleziona?
Yisroel,

non sono sicuro di voler rendere conto di [disabilitato] ma non credo che debba essere inviato / ritirato.
meder omuraliev,

potrebbe essere più semplice utilizzare serializeArray () per ottenere la mappa, quindi utilizzare un codice simile a quello sopra per convertirlo in un normale oggetto JSON, in questo modo non ho a che fare con il modulo stesso
Yisroel,

2
L'uso dell'array serializzato avrebbe funzionato, ma essenzialmente avresti ripetuto la raccolta due volte, una volta per produrre l'array, quindi l'array. Non ne vedo la necessità.
tvanfosson,

Regolerò il selettore per abilitato / disabilitato.
tvanfosson,

23

Se si utilizza Underscore.js è possibile utilizzare il relativamente conciso:

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

19

Ho verificato che ci sia un problema con tutte le altre risposte, che se il nome di input è un array, come ad esempio name[key], dovrebbe essere generato in questo modo:

name:{ key : value }


Ad esempio: se si dispone di un modulo HTML simile a quello seguente:

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

Ma dovrebbe essere generato proprio come il JSON di seguito, e non diventa un oggetto come il seguente con tutte le altre risposte. Quindi, se qualcuno vuole portare qualcosa come il seguente JSON, prova il codice JS qui sotto.

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


Questa risposta copre il caso menzionato, ma non copre casi come la casella di controllo [] o persino uno [l'altro] [altro_uno]
Leonardo Beal,

1
@LeonardoBeal ho sistemato il mio ans .. controlla ora ..!
Bhavik Hirani,

Sono il downvoter e ho effettuato il downvoting perché evalnon dovrebbe essere usato in questo modo perché eval, se usato in questo modo, promuove pratiche terribilmente inaffidabili, buggy, non performanti e potenzialmente non sicure.
Jack Giffin,

2
Non posso essere d'accordo che questa sia una buona risposta. E per favore, quando scrivi le risposte, rendi il tuo codice autoesplicativo o lo spieghi. this.c = function(k,v){ eval("c = typeof "+k+";"); if(c == 'undefined') _t.b(k,v);}è breve e non esplicativo. Un dev con meno esperienza lo copierà senza capire perché e come funziona.
iRaS,

2
@JackGiffin Dai un'occhiata al mio nuovo codice ora perché l'ho rimosso eval()dal mio codice.
Bhavik Hirani,

17

Ok, so che questa ha già una risposta molto votata, ma un'altra domanda simile è stata posta di recente e sono stato indirizzato anche a questa domanda. Vorrei offrire anche la mia soluzione, perché offre un vantaggio rispetto alla soluzione accettata: è possibile includere elementi di modulo disabilitati (che a volte è importante, a seconda del funzionamento dell'interfaccia utente)

Ecco la mia risposta dall'altra domanda SO :

Inizialmente, stavamo usando il serializeArray()metodo di jQuery , ma questo non include gli elementi del modulo che sono disabilitati. Disabiliteremo spesso gli elementi del modulo che sono "sincronizzati" con altre fonti sulla pagina, ma dobbiamo comunque includere i dati nel nostro oggetto serializzato. Quindi serializeArray()è fuori. Abbiamo usato il :inputselettore per ottenere tutti gli elementi di input (sia abilitati che disabilitati) in un determinato contenitore e quindi $.map()per creare il nostro oggetto.

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

Si noti che affinché ciò funzioni, ognuno dei tuoi input avrà bisogno di un nameattributo, che sarà il nome della proprietà dell'oggetto risultante.

Questo è in realtà leggermente modificato rispetto a quello che abbiamo usato. Avevamo bisogno di creare un oggetto che fosse strutturato come .NET IDictionary, quindi abbiamo usato questo: (lo fornisco qui nel caso sia utile)

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

Mi piacciono entrambe queste soluzioni, perché sono semplici usi della $.map()funzione e tu hai il controllo completo sul tuo selettore (quindi, quali elementi finisci incluso nell'oggetto risultante). Inoltre, non è richiesto alcun plug-in aggiuntivo. Semplicemente vecchio jQuery.


6
Ho provato questo in un progetto, usando in mapquesto modo si crea una matrice di oggetti con una singola proprietà, non comprime tutte le proprietà in un unico oggetto.
joshperry,

16

Questa funzione dovrebbe gestire array multidimensionali insieme a più elementi con lo stesso nome.

Finora lo uso da un paio d'anni:

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

15

Puoi farlo:

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

Vedi JSON .


14

One-liner (nessuna dipendenza diversa da jQuery), utilizza l'associazione di oggetti fissi per la funzione passata al mapmetodo.

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

Cosa fa?

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

adatto per app web progressive (si può facilmente supportare sia azioni di invio di moduli regolari sia richieste ajax)


12

Uso:

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

Produzione:

{"myfield": "myfield value", "passwordfield": "mypasswordvalue"}

7

La semplicità è la migliore qui. Ho usato una semplice stringa di sostituzione con un'espressione regolare, e finora hanno funzionato come un incantesimo. Non sono un esperto di espressioni regolari, ma scommetto che puoi anche popolare oggetti molto complessi.

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

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

7

Da una risposta più vecchia :

$('form input, form select').toArray().reduce(function(m,e){m[e.name] = $(e).val(); return m;},{})

Da quello che posso dire, la differenza è che la tua soluzione non dipende da serializeArraycosì hai la libertà di scegliere qualunque input tu voglia (es. Puoi includere input disabilitati), giusto? Cioè questo non è accoppiato a nessun modulo o evento di presentazione, è solo indipendente da solo?
dvtan,

l'unica piccola differenza con la risposta collegata è che non ci sono dati necessari per istanziare, reducerestituisce l'oggetto. Questo non è indipendente poiché toArrayproviene da jQuery.
siti

6

Ho riscontrato un problema con il codice di Tobias Cohen (non ho abbastanza punti per commentarlo direttamente), che altrimenti funziona per me. Se hai due opzioni di selezione con lo stesso nome, entrambe con value = "", il codice originale produrrà "name": "" invece di "name": ["", ""]

Penso che questo possa essere risolto aggiungendo "|| o [this.name] == ''" alla prima condizione 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;
};

6

Usando la soluzione di maček , l'ho modificata per funzionare con il modo in cui ASP.NET MVC gestisce i loro oggetti nidificati / complessi nella stessa forma. Tutto quello che devi fare è modificare il pezzo validato in questo modo:

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

Ciò corrisponderà e quindi mapperà correttamente gli elementi con nomi come:

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

E

<input type="text" name="zooAnimals[0].name" />

5

il modo più semplice e preciso ho trovato per questo problema è stato quello di utilizzare plug-in barbecue o questo uno (che è circa 0.5K byte dimensioni).

funziona anche con array multidimensionali.

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


Questo sembra funzionare bene. Esiste un repository alternativo per jquery-deparam che include file di descrizione per bower e npm.
Alf Eaton,


4
const formData = new FormData(form);

let formDataJSON = {};

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

    formDataJSON[key] = value;
}

3

Preferisco questo approccio perché: non devi iterare più di 2 raccolte, puoi ottenere cose diverse da "nome" e "valore" se necessario, e puoi disinfettare i tuoi valori prima di memorizzarli nell'oggetto ( se si hanno valori predefiniti che non si desidera memorizzare, ad esempio).

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

Usa così:

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

Testato solo su Firefox.


3

Trasforma qualsiasi cosa in un oggetto (non testato dall'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>

L'output del test:

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

su

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

produrrà:

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

3

Ho riscontrato un problema con la soluzione selezionata.

Quando si utilizzano moduli con nomi basati su array, la funzione jQuery serializeArray () in realtà muore.

Ho un framework PHP che utilizza nomi di campo basati su array per consentire che lo stesso modulo venga inserito più volte nella stessa pagina in più viste. Questo può essere utile per mettere sia aggiungere, modificare ed eliminare nella stessa pagina senza modelli di modulo in conflitto.

Dato che volevo serializzare i moduli senza dover eliminare questa funzionalità di base assoluta, ho deciso di scrivere il mio 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;
            }
        });

Nota: questo funziona anche al di fuori del modulo submit (), quindi se si verifica un errore nel resto del codice, il modulo non verrà inviato se si inserisce un pulsante di collegamento che dice "salva modifiche".

Si noti inoltre che questa funzione non deve mai essere utilizzata per convalidare il modulo solo per raccogliere i dati da inviare al lato server per la convalida. L'uso di un codice così debole e assegnato in serie causerà XSS , ecc.


3

Di recente ho avuto lo stesso problema e sono uscito con questo plugin .toJSON jQuery che converte un modulo in un oggetto JSON con la stessa struttura. Ciò è particolarmente utile per i moduli generati dinamicamente in cui si desidera consentire all'utente di aggiungere più campi in luoghi specifici.

Il punto è che potresti effettivamente voler costruire un modulo in modo che abbia una struttura stessa, quindi diciamo che vuoi creare un modulo in cui l'utente inserisce i suoi luoghi preferiti in città: puoi immaginare questo modulo per rappresentare un <places>...</places>elemento XML contenente un elenco di luoghi a cui l'utente piace quindi un elenco di <place>...</place>elementi ciascuno contenente ad esempio un <name>...</name>elemento, un <type>...</type>elemento e quindi un elenco di <activity>...</activity>elementi per rappresentare le attività che è possibile eseguire in tale luogo. Quindi la tua struttura XML sarebbe così:

<places>

    <place>

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

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

    </place>

    <place>...</place>

    <place>...</place>

</places>

Quanto sarebbe bello avere un oggetto JSON da questo che rappresenterebbe questa esatta struttura in modo da poter:

  • Conservare questo oggetto così com'è in qualsiasi CouchDB database simile a
  • Leggilo dal tuo server $ _POST [] e recupera un array nidificato correttamente che puoi manipolare semanticamente
  • Utilizzare alcuni script lato server per convertirlo in un file XML ben formato (anche se non si conosce a priori la sua struttura esatta)
  • Usalo in qualche modo così com'è in qualsiasi script server simile a Node.js

OK, quindi ora dobbiamo pensare a come un modulo può rappresentare un file XML.

Ovviamente il <form>tag è il root, ma poi abbiamo quello<place> quell'elemento che è un contenitore e non un elemento di dati stesso, quindi non possiamo usare un tag di input per esso.

Ecco dove il <fieldset>tag è utile! Useremo i <fieldset>tag per rappresentare tutti gli elementi contenitore nella nostra rappresentazione modulo / XML e quindi ottenere un risultato come questo:

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

Come puoi vedere in questo modulo, stiamo infrangendo la regola dei nomi univoci, ma questo è OK perché verranno convertiti in una matrice di elementi, quindi verranno referenziati solo dal loro indice all'interno della matrice.

A questo punto puoi vedere come non esiste name="array[]"un nome simile all'interno del modulo e tutto è carino, semplice e semantico.

Ora vogliamo che questo modulo sia convertito in un oggetto JSON che assomiglierà a questo:

{'places':{

    'place':[

        {

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

            'activity':[

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

            ]

        },

        {...},

        {...}

    ]

}}

Per fare questo ho sviluppato questo plugin jQuery qui che qualcuno ha aiutato a ottimizzare in questo thread di revisione del codice e assomiglia a questo:

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

Ho anche pubblicato questo post sul blog per spiegarlo di più.

Questo converte tutto in un modulo in JSON (anche radio e caselle di controllo) e tutto ciò che ti resta da fare è chiamare

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

So che ci sono molti modi per convertire i moduli in oggetti JSON e sicuro .serialize()e .serializeArray()funzionano alla grande nella maggior parte dei casi e sono principalmente destinati ad essere utilizzati, ma penso che tutta questa idea di scrivere un modulo come una struttura XML con nomi significativi e convertirlo in un oggetto JSON ben formato Vale la pena provare un , anche il fatto che sia possibile aggiungere tag di input con lo stesso nome senza preoccuparsi è molto utile se è necessario recuperare i dati dei moduli generati dinamicamente.

Spero che questo aiuti qualcuno!


Cosa stai cercando di fare?
Onheiron,


3

Un'altra risposta

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


3

[AGGIORNAMENTO 2020]

Con un semplice oneliner in vaniglia js:

Object.fromEntries(new FormData(form))

2

Mi piace la versione di Samuels, ma credo che abbia un piccolo errore. Normalmente JSON viene inviato come

{ "coreSKU": "PCGUYJS", "name_de": "qualunque", ...

NON come

[{ "CoreSKU": "PCGUYJS"}, { "name_de": "qualunque"}, ...

quindi la funzione IMO dovrebbe leggere:

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

e racchiuderlo nell'array di dati (come anche comunemente previsto) e infine inviarlo come astring App.stringify ({data: App.toJson ('#cropform: input')})

Per il stringify, guarda la domanda 3593046 per la versione snella, su json2.js per la versione coperta da ogni evenienza. Questo dovrebbe coprire tutto :)


Grazie .. questo rende (come hai detto) una differenza minuscola ma molto importante.
Adarsha,

2

Per una soluzione rapida e moderna, utilizzare JSONify JQuery . L'esempio che segue è preso alla lettera dal README di GitHub. Tutto il merito a Kushal Pandya, autore del plugin.

Dato:

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

In esecuzione:

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

produce:

{"name":"Joe User","email":"joe@example.com","password":"mypass"}

Se vuoi fare un POST jQuery con questo oggetto JSON:

$('#mybutton').click(function() {
    $.post('/api/user', JSON.stringify($('#myform').jsonify()));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.