Konwertuj dane formularza na obiekt JavaScript za pomocą jQuery

1624

Jak przekonwertować wszystkie elementy mojego formularza na obiekt JavaScript?

Chciałbym mieć jakiś sposób automatycznego budowania obiektu JavaScript z mojej formy, bez konieczności zapętlania każdego elementu. Nie chcę ciągu w postaci zwróconej przez $('#formid').serialize();, ani nie chcę, aby mapa została zwrócona przez$('#formid').serializeArray();

Yisroel
źródło
16
ponieważ pierwszy zwraca ciąg znaków, dokładnie tak, jak byś otrzymał, gdybyś przesłał formularz metodą GET, a drugi daje tablicę obiektów, każdy z parą wartości nazwa. Chcę, że jeśli mam pole o nazwie „e-mail”, dostaję obiekt, który pozwoli mi pobrać tę wartość z obj.email. Z serializeArray () musiałbym zrobić coś takiego jak obj [indexOfElement] .value
Yisroel
2
@James - Akceptowana odpowiedź przy użyciu biblioteki JSON-js D. Crockforda. Oto przykład: github.com/tleese22/google-app-engine-jappstart/blob/master/src/…
Taylor Leese
4
@Taylor Tak, powiedziałbym, że poprawna odpowiedź korzysta z lib Crockforda i funkcji Tobiasza: JSON.stringify ($ ('myForm'). SerializeObject ())
James McCormack
5
@Jonz - Istnieją inne powody poza przesłaniem / przesłaniem użycia elementu formularza. Jeśli wykonujesz ciężkie podnoszenie przy użyciu wartości formularza w JavaScript (np. Aplikacja jednostronicowa), bardzo przydatne jest posiadanie ich w formacie obiektowym do uzyskiwania dostępu i manipulowania nimi. Ponadto ciągi zapytań HTTP Post i Get nie są jedynymi formatami przenoszenia danych.
Patrick M
3
Dobry js natknąłem się >> github.com/marioizquierdo/jquery.serializeJSON
Bongs

Odpowiedzi:

1657

serializeArrayjuż to robi. Musisz tylko zamasować dane do wymaganego formatu:

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

Uważaj na ukryte pola, które mają taką samą nazwę jak rzeczywiste dane wejściowe, ponieważ zostaną zastąpione.

Tobias Cohen
źródło
4
jak mówi tvanfosson, po co dwa razy powtarzać kolekcję?
Yisroel
69
Czy masz na myśli „po co używać serializeArray, aby uzyskać dane w pierwszej kolejności?” Ponieważ serializeArray jest już napisany, jest testowany jednostkowo w wielu przeglądarkach i teoretycznie mógłby zostać ulepszony w późniejszych wersjach jQuery. Im mniej kodu, który piszesz, który ma bezpośredni dostęp do niespójnych elementów, takich jak elementy DOM, tym bardziej stabilny będzie twój kod.
Tobias Cohen
56
Ostrzegamy, serializeArray () nie będzie zawierał wyłączonych elementów. Często wyłączam elementy wejściowe, które są zsynchronizowane z innymi elementami na stronie, ale nadal chcę, aby były uwzględnione w moim serializowanym obiekcie. Lepiej jest użyć czegoś takiego, $.map( $("#container :input"), function(n, i) { /* n.name and $(n).val() */ } );jeśli chcesz uwzględnić wyłączone elementy.
Samuel Meacham
22
@TobiasCohen Nie obsługuje foo[bar]danych wejściowych typu zgodnie z oczekiwaniami, nie wspominając o większości innych odmian nazw wejściowych. Po bardzo frustracji płytkimi rozwiązaniami tego problemu napisałem własną wtyczkę jQuery - szczegóły w odpowiedzi, której udzieliłem na to pytanie.
maček
6
@macek Wiem, że ma to kilka miesięcy, ale od kiedy tablice używają indeksów nienumerycznych? Nikt nie powinien nazywać wejściowego foo [bar] i mieć nadzieję potraktować go jako tablicę. Czy mylisz tablice i skróty? Tak, [] jest powszechnie rozumiane jako akcesorium, ale nie tylko dla tablic. Mówienie, że jest to prawidłowy HTML, ale nie jest w specyfikacji HTML, jest sprzecznością. Tak, przeglądarka może się nie dusić, ale niewielu serwerów będzie wiedziało, jak dokonać deserializacji, tak jak by to zrobiła tablica. Dlaczego? Ponieważ nie ma go w specyfikacji HTML. Dlatego jest rzeczywiście nieważny.
kroehre
446

Konwertuj formularze na JSON jak szef


Obecne źródło znajduje się na GitHub i Bower .

$ bower zainstaluj jquery-serialize-object


Poniższy kod jest już nieaktualny .

Poniższy kod może zająć się wszystkimi rodzajami nazw wejściowych; i obchodź się z nimi tak, jak można się spodziewać.

Na przykład:

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

Stosowanie

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

The Sorcery (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
źródło
17
To działa całkiem dobrze. Ale jest źle nazwany: nie zwraca JSON, jak sama nazwa wskazuje. Zamiast tego zwraca literał obiektu. Ważne jest również, aby sprawdzić hasOwnProperty, w przeciwnym razie twoje tablice mają wszystko, co jest dołączone do ich prototypu, takie jak: {numbers: ["1", "3", indexOf: function () {...}]}
frontendbeauty
5
@frontendbeauty faktycznie, toJSON jest dokładnie tym, co według specyfikacji powinno się nazywać: developer.mozilla.org/en/JSON#toJSON()_method niefortunnym błędem.
Ryan Florence
4
@Marek, zrobiłem test tutaj na jsfiddle . Sztuka polega na tym, aby właściwie nazwać swój wybór. <select name="foo" multiple="multiple">nie będzie działać w żadnym scenariuszu. Jeśli jednak użyjesz [], jak w <select name="bar[]" multiple="multiple">, to będzie działać dobrze :)
maček
3
Chciałem zauważyć, że github.com/serbanghita/formToObject jest podobną metodą JavaScript (nie wymaga bibliotek zewnętrznych), która wykonuje to samo zadanie. Obsługuje „wiele”, ignoruje elementy „wyłączone”.
banerban Ghiță
8
To rozwiązanie powinno być na górze, ponieważ dotyczy problemu zagnieżdżonych kluczy jako nazw elementów formularza.
SquareCat
283

Co jest źle z:

var data = {};
$(".form-selector").serializeArray().map(function(x){data[x.name] = x.value;}); 
mkschreder
źródło
2
@LayZee - jeśli nic nie jest zaznaczone, dlaczego miałbyś chcieć tego na swoim zapleczu? jeśli musisz wybrać opcję, sprawdź poprawność danych wejściowych przed serializacją.
Demencja
21
jeśli nic nie zwracasz, dlaczego po prostu nie użyjesz .eachzamiast .map??
azerafati,
10
To takie proste, ponieważ jest bardzo naiwne ... nie obsługuje pól wyboru o tej samej nazwie. Za każdym razem zastąpi zaznaczony element przed nim. Zdecydowanie potrzebujesz detekcji typu danych wejściowych, aby upewnić się, że jest odpowiednio serializowana.
Joshua F. Rountree,
5
Jeśli potrzebujesz czystego rozwiązania jQuery, możesz użyćvar 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;}, {})
strony
103

Naprawiona wersja rozwiązania Tobiasa Cohena. Ten poprawnie obsługuje wartości fałszowania, takie jak 0i ''.

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

Oraz wersja CoffeeScript dla Twojej wygody kodowania:

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
źródło
W coffeescript, jeśli najpierw można skrócić blokvalue = @value ? ''
nzifnab
A jeśli próbujesz serializować formularze podobne do Railsów, możesz chcieć usunąć element root z wygenerowanych kluczy. Aby to osiągnąć, Dodałem nową param keyMapi następujący wiersz: key = if keyMap? then keyMap(@name) else @name. Teraz możesz przekazać funkcję mapowania jak (name) -> name.match(/\[([^\]]+)]/)[1]. I wtedy należałoby zmienić wszystkie późniejsze @namecelu key, oczywiście
Damir Zekic
@ DamirZekić, jeśli twoim głównym elementem był post, możesz po prostu to zrobić $('form').serializeObject().post. Nie ma potrzeby wymyślnego mapowania.
maček,
@ DamirZekić Co robi ta linia? if (!objectData[this.name].push)??
kittu
@kittu Sprawdza, czy objectData[this.name]ma metodę push (z grubsza, czy jest to tablica). Jeśli jest to tablica, wypycha wartość, jeśli nie jest tablicą, konwertuje ją na tablicę, dzięki czemu wiele wartości z tym samym kluczem zostanie połączonych w tablicę.
Daniel X Moore,
63

Lubię używać, Array.prototype.reduceponieważ jest to jedna linijka i nie opiera się na Underscore.js lub podobnym:

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

Jest to podobne do użycia odpowiedzi Array.prototype.map, ale nie musisz zaśmiecać swojego zasięgu dodatkową zmienną obiektową. One-stop shopping.

WAŻNA UWAGA : Formularze z danymi wejściowymi, które mają zduplikowane nameatrybuty, są poprawnymi kodami HTML i są w rzeczywistości powszechnym podejściem. W takim przypadku użycie dowolnej odpowiedzi w tym wątku będzie nieodpowiednie (ponieważ klucze obiektów muszą być unikalne).

Ethan Brown
źródło
To jest dość eleganckie. Warto jednak zauważyć, że array.prototype.reduce()nie jest dostępny w IE8, ponieważ jest częścią specyfikacji ECMAScript 5. Więc jeśli potrzebujesz wsparcia IE8, będziesz chciał użyć polifillu lub innego rozwiązania.
gfullam
3
To prawda, ale łatwo jest go wypełnić. Ponadto ból głowy IE8 prawie się skończył, dzięki świetnej pracy Microsoftu z IE11 Enterprise Mode - już nie obsługuję indywidualnych użytkowników z IE8, ale kiedy pojawia się organizacja z 10 000 pracowników, którzy wszyscy używają IE8 ... to jest inne. Na szczęście Microsoft ciężko pracuje nad tym problemem.
Ethan Brown,
1
Cóż, powinieneś spojrzeć na informatyków w trybie IE11 Enterprise - zapewnia nowoczesną przeglądarkę ORAZ sposób uruchamiania aplikacji kompatybilnych z IE8. Sprytne posunięcie ze strony Microsoftu: howtogeek.com/184634/…
Ethan Brown
30

Wszystkie te odpowiedzi wydawały mi się przesadne. Jest coś, co można powiedzieć o prostocie. Dopóki wszystkie dane wejściowe formularza mają ustawiony atrybut name, powinno to działać po prostu 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
źródło
4
Nie obsługuje jednak danych zagnieżdżonych, dlatego odpowiedzi stają się bardziej skomplikowane.
frontendbeauty
Działa świetnie dla mnie. Potrzebowałem tylko przechowywania par klucz-wartość; strajkowanie, a następnie zapisywanie do localStorage lub cookie działało po prostu dandy.
Greg Pettit
23

Naprawdę nie ma takiej możliwości bez zbadania każdego z elementów. To, co naprawdę chcesz wiedzieć, to „czy ktoś już napisał metodę, która konwertuje formularz na obiekt JSON?” Powinno działać coś podobnego - zwróć uwagę, że da ci tylko elementy formularza, które zostaną zwrócone za pomocą POST (musi mieć nazwę). To nie jest testowane .

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
źródło
prawda, chciałbym wtyczki, która by to dla mnie zrobiła. ograniczenie posiadania imienia nie jest niczym wielkim. czy spowoduje to pobranie wszystkich pól w formularzu, w tym pól tekstowych i zaznaczeń?
Yisroel
nie jestem pewien, czy chcesz wziąć pod uwagę [wyłączone], ale nie sądzę, że należy to wysłać / odebrać.
meder omuraliev
może być łatwiej użyć serializeArray (), aby uzyskać mapę, a następnie użyć kodu podobnego do powyższego, aby przekonwertować go na normalny obiekt JSON, w ten sposób nie mam do czynienia z samym formularzem
Yisroel
2
Korzystanie z serializedArray działałoby, ale w zasadzie należy powtarzać dwa razy kolekcję - raz, aby utworzyć tablicę, a następnie tablicę. Nie widzę takiej potrzeby.
tvanfosson
Dostosuję selektor do włączenia / wyłączenia.
tvanfosson
23

Jeśli używasz Underscore.js , możesz użyć stosunkowo zwięzłego:

_.object(_.map($('#myform').serializeArray(), _.values))
olleicua
źródło
19

Sprawdziłem, że istnieje problem ze wszystkimi pozostałymi odpowiedziami, że jeśli nazwa wejściowa ma postać tablicy, na przykład name[key], to powinna zostać wygenerowana w następujący sposób:

name:{ key : value }


Na przykład: jeśli masz formularz HTML podobny do poniższego:

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

Ale powinien być generowany tak jak JSON poniżej i nie staje się obiektem podobnym do poniższego ze wszystkimi innymi odpowiedziami. Więc jeśli ktoś chce przynieść coś takiego jak następujący JSON, wypróbuj poniższy kod JS.

{
    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
źródło
Ta odpowiedź obejmuje wspomniany przypadek, ale nie obejmuje spraw takich jak pole wyboru [] lub nawet jeden [inny] [inny_jeden]
Leonardo Beal
1
@LeonardoBeal naprawiam mój ans .. sprawdź to teraz ..!
Bhavik Hirani
Jestem zwycięzcą i przegłosowałem, ponieważ evalnie należy go używać w ten sposób, ponieważ evalw ten sposób promuje strasznie niewiarygodne, błędne, źle działające i potencjalnie niebezpieczne praktyki.
Jack Giffin
2
Nie mogę się zgodzić, że to dobra odpowiedź. I proszę, pisząc odpowiedzi, uczyń swój kod zrozumiałym lub wyjaśnij go. this.c = function(k,v){ eval("c = typeof "+k+";"); if(c == 'undefined') _t.b(k,v);}jest krótki i niewytłumaczalny. Dev z mniejszym doświadczeniem skopiuje to bez zrozumienia, dlaczego i jak to działa.
iRaS
2
@JackGiffin Sprawdź mój nowy kod teraz, ponieważ usunąłem go eval()z mojego kodu.
Bhavik Hirani
17

Ok, wiem, że ma to już bardzo pozytywną odpowiedź, ale zadano inne podobne pytanie ostatnio i skierowano mnie również na to pytanie. Chciałbym również zaoferować moje rozwiązanie, ponieważ ma ono przewagę nad zaakceptowanym rozwiązaniem: możesz dołączyć wyłączone elementy formularza (co jest czasami ważne, w zależności od tego, jak działa Twój interfejs użytkownika)

Oto moja odpowiedź na inne SO pytanie :

Początkowo używaliśmy metody jQuery serializeArray(), ale nie obejmuje ona elementów formularza, które są wyłączone. Często wyłączamy elementy formularza, które są „synchronizowane” z innymi źródłami na stronie, ale nadal musimy uwzględnić dane w naszym serializowanym obiekcie. Tak serializeArray()jest. Użyliśmy :inputselektora, aby uzyskać wszystkie elementy wejściowe (zarówno włączone, jak i wyłączone) w danym kontenerze, a następnie $.map()stworzyć nasz obiekt.

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

Zauważ, że aby to zadziałało, każde twoje wejście będzie wymagało nameatrybutu, który będzie nazwą właściwości wynikowego obiektu.

To jest właściwie nieco zmodyfikowane w stosunku do tego, z czego korzystaliśmy. Musieliśmy utworzyć obiekt o strukturze .NET IDictionary, więc użyliśmy tego: (Podaję go tutaj, na wypadek, gdyby był użyteczny)

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

Podobają mi się oba te rozwiązania, ponieważ są one prostymi zastosowaniami $.map()funkcji, a ty masz pełną kontrolę nad swoim selektorem (więc jakie elementy trafisz w tym do wynikowego obiektu). Ponadto nie jest wymagana dodatkowa wtyczka. Zwykły stary jQuery.

Samuel Meacham
źródło
6
Próbowałem tego w projekcie, używając maptego w taki sposób tworzy tablicę obiektów z pojedynczą właściwością, to nie powoduje zwinięcia wszystkich właściwości w jeden obiekt.
joshperry
16

Ta funkcja powinna obsługiwać tablice wielowymiarowe wraz z wieloma elementami o tej samej nazwie.

Używam go od kilku lat:

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
źródło
15

Możesz to zrobić:

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

Zobacz JSON .

Harini Sekar
źródło
14

Jednoliniowy (bez zależności innych niż jQuery), używa stałego wiązania obiektu dla funkcji przekazywanej do mapmetody.

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

Co to robi?

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

nadaje się do progresywnych aplikacji internetowych (z łatwością można obsługiwać zarówno zwykłe przesyłanie formularzy, jak i żądania ajax)

test30
źródło
12

Posługiwać się:

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

Wynik:

{"myfield": "myfield value", "passwordfield": "mypasswordvalue"}
Adrian Seeley
źródło
7

Prostota jest tutaj najlepsza. Użyłem prostego łańcucha zastępującego wyrażenie regularne i do tej pory działały jak urok. Nie jestem ekspertem od wyrażeń regularnych, ale założę się, że możesz zapełniać nawet bardzo złożone obiekty.

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

values.replace(/([^&]+)=([^&]*)/g, function (match, name, value) {
    attributes[name] = value;
});
ngr
źródło
7

Od jakiejś starszej odpowiedzi:

$('form input, form select').toArray().reduce(function(m,e){m[e.name] = $(e).val(); return m;},{})
juanpastas
źródło
Z tego, co mogę powiedzieć, różnica polega na tym, że twoje rozwiązanie nie zależy od serializeArraytego, więc masz swobodę wyboru dowolnych wejść (np. Możesz włączyć wejścia wyłączone), prawda? Czyli nie jest to powiązane z żadną formą ani zdarzeniem przesyłania, jest po prostu samo w sobie niezależne?
dvtan
jedyną niewielką różnicą z połączoną odpowiedzią jest to, że nie ma danych potrzebnych do utworzenia wystąpienia, reducezwraca obiekt. Nie jest to niezależne, ponieważ toArraypochodzi z jQuery.
witryny
6

Znalazłem problem z kodem Tobiasza Cohena (nie mam wystarczającej liczby punktów, aby go skomentować bezpośrednio), co w innym przypadku działa dla mnie. Jeśli masz dwie opcje wyboru o tej samej nazwie, obie o wartości = „”, oryginalny kod wygeneruje „name”: „” zamiast „name”: [”„, ””]

Myślę, że można to naprawić, dodając „|| o [this.name] == ''” do pierwszego, jeśli warunek:

$.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;
};
użytkownik1134789
źródło
6

Korzystając z rozwiązania Mačka , zmodyfikowałem go, aby działał w sposób, w jaki ASP.NET MVC obsługuje swoje zagnieżdżone / złożone obiekty w tej samej formie. Wszystko, co musisz zrobić, to zmodyfikować poprawny fragment do tego:

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

Spowoduje to dopasowanie, a następnie prawidłowe odwzorowanie elementów o nazwach takich jak:

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

I

<input type="text" name="zooAnimals[0].name" />
G-Ram
źródło
5

najprostszy i najdokładniejszy sposób znalazłem tego problemu było zastosowanie wtyczki grilla lub ten jeden (który jest o 0.5K bajtów rozmiar).

działa również z tablicami wielowymiarowymi.

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

Roey
źródło
To wydaje się działać dobrze. Istnieje alternatywne repozytorium dla jquery-deparam, które zawiera pliki opisów dla bower i npm.
Alf Eaton,
4

Jest taka wtyczka do jQuery, jquery.serializeJSON . Z powodzeniem wykorzystałem go w kilku projektach. To działa jak urok.

Peter Mortensen
źródło
4
const formData = new FormData(form);

let formDataJSON = {};

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

    formDataJSON[key] = value;
}
Stonetip
źródło
3

Wolę takie podejście, ponieważ: nie musisz iterować ponad 2 kolekcje, możesz uzyskać inne rzeczy niż „nazwa” i „wartość”, jeśli to konieczne, i możesz zdezynfekować swoje wartości przed zapisaniem ich w obiekcie ( jeśli masz na przykład wartości domyślne, których nie chcesz przechowywać).

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

Użyj tak:

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

Testowane tylko w przeglądarce Firefox.

kflorence
źródło
3

Zamień wszystko w obiekt (nie testowany jednostkowo)

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

Wynik testu:

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

na

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

da:

{ input: { a: text, b: select }, otherinput: { c: { a: a, 0: b, 1: d }, d: { b: c } }, anotherinput: 1 }
{ input: { b: select } }
eithed
źródło
3

Znalazłem problem z wybranym rozwiązaniem.

Podczas korzystania z formularzy, które mają nazwy oparte na tablicach, funkcja serializeArray () jQuery faktycznie umiera.

Mam framework PHP, który używa tablicowych nazw pól, aby umożliwić wielokrotne umieszczanie tej samej formy na tej samej stronie w wielu widokach. Może to być przydatne do dodawania, edytowania i usuwania na tej samej stronie bez sprzecznych modeli formularzy.

Ponieważ chciałem seralizować formularze bez konieczności wyłączania tej absolutnej podstawowej funkcjonalności, postanowiłem napisać własną 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;
            }
        });

Uwaga: Działa to również poza formularzem submission (), więc jeśli wystąpi błąd w pozostałej części kodu, formularz nie zostanie przesłany, jeśli umieścisz przycisk linku z napisem „zapisz zmiany”.

Należy również pamiętać, że tej funkcji nie należy nigdy używać do sprawdzania poprawności formularza tylko w celu zebrania danych do wysłania po stronie serwera w celu sprawdzenia poprawności. Użycie takiego słabego i przypisanego masowo kodu spowoduje XSS itp.

Sammaye
źródło
3

Miałem ostatnio ten sam problem i wypuściłem wtyczkę .toJSON jQuery, która konwertuje formularz na obiekt JSON o tej samej strukturze. Jest to szczególnie przydatne w przypadku dynamicznie generowanych formularzy, w których użytkownik chce, aby użytkownik dodawał więcej pól w określonych miejscach.

Chodzi o to, że możesz chcieć zbudować formularz tak, aby sam miał strukturę, więc powiedzmy, że chcesz stworzyć formularz, w którym użytkownik wstawi swoje ulubione miejsca w mieście: możesz sobie wyobrazić, że ten formularz reprezentuje <places>...</places>element XML zawierający lista miejsc, które użytkownik lubi, a zatem lista <place>...</place>elementów, z których każda zawiera na przykład <name>...</name>element, <type>...</type>element, a następnie listę <activity>...</activity>elementów reprezentujących czynności, które można wykonać w takim miejscu. Twoja struktura XML wyglądałaby tak:

<places>

    <place>

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

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

    </place>

    <place>...</place>

    <place>...</place>

</places>

Jak fajnie byłoby mieć z tego obiekt JSON, który reprezentowałby tę dokładną strukturę, abyś mógł:

  • Przechowuj ten obiekt tak, jak jest w dowolnej bazie danych podobnej do CouchDB
  • Przeczytaj go po stronie serwera $ _POST [] i pobierz poprawnie zagnieżdżoną tablicę, którą możesz następnie modyfikować semantycznie
  • Użyj jakiegoś skryptu po stronie serwera, aby przekonwertować go na dobrze sformatowany plik XML (nawet jeśli nie znasz jego dokładnej struktury a priori)
  • Po prostu użyj go tak, jak jest w dowolnym skrypcie serwera podobnym do Node.js

OK, teraz musimy pomyśleć, jak formularz może reprezentować plik XML.

Oczywiście <form>znacznik jest root, ale mamy ten <place>element, który jest kontenerem, a nie samym elementem danych, więc nie możemy dla niego użyć znacznika wejściowego.

Oto, gdzie <fieldset>przydaje się tag! Użyjemy <fieldset>znaczników do reprezentowania wszystkich elementów kontenera w naszej reprezentacji formularza / XML, aby uzyskać taki wynik:

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

Jak widać w tym formularzu, łamiemy zasadę unikatowych nazw, ale jest to w porządku, ponieważ zostaną przekonwertowane na tablicę elementu, a więc będą się do nich odwoływały tylko ich indeksy wewnątrz tablicy.

W tym momencie możesz zobaczyć, jak name="array[]"w formularzu nie ma podobnej nazwy, a wszystko jest ładne, proste i semantyczne.

Teraz chcemy, aby ten formularz został przekonwertowany na obiekt JSON, który będzie wyglądał następująco:

{'places':{

    'place':[

        {

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

            'activity':[

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

            ]

        },

        {...},

        {...}

    ]

}}

Aby to zrobić, opracowałem tutaj wtyczkę jQuery, którą ktoś pomógł zoptymalizować w tym wątku Code Review i wygląda następująco:

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

Napisałem też ten post na blogu, aby wyjaśnić to więcej.

Konwertuje to wszystko w formie do JSON (nawet radio i pola wyboru) i wszystko, co pozostało do zrobienia, to połączenie

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

Wiem, że istnieje wiele sposobów konwertowania formularzy na obiekty JSON, .serialize()ale .serializeArray()w większości przypadków działają one doskonale i są przeznaczone głównie do użycia, ale myślę, że cała ta idea pisania formularza jako struktury XML o znaczących nazwach i przekształcania go w dobrze sformułowany obiekt JSON jest warty wypróbowania, a także fakt, że można bez obaw dodawać tagi wejściowe o tej samej nazwie, jest bardzo przydatny, jeśli trzeba pobrać dane formularzy generowane dynamicznie.

Mam nadzieję, że to komuś pomoże!

Onheiron
źródło
co próbujesz zrobić?
Onheiron
3

Sam kodowałem formularz do wielowymiarowego obiektu JavaScript, aby użyć go w środowisku produkcyjnym. Wynikiem jest https://github.com/serbanghita/formToObject.js .

serbanghita
źródło
Wykorzystałem jego odmianę do bardzo specyficznej implementacji, wielkie dzięki!
Chris Baker,
Dziękujemy za poinformowanie mnie, że jest to przydatne. Mam do dyspozycji kilka nowych funkcji, które mnie motywują.
banerban Ghiță
3

Kolejna odpowiedź

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

test 30
źródło
3

[AKTUALIZACJA 2020]

Z prostym oneliner w waniliowym js:

Object.fromEntries(new FormData(form))
aret
źródło
2

Podoba mi się wersja samuels, ale uważam, że ma mały błąd. Zwykle JSON jest wysyłany jako

{„coreSKU”: „PCGUYJS”, „name_de”: „cokolwiek”, ...

NIE jak

[{„coreSKU”: „PCGUYJS”}, {„name_de”: „cokolwiek”}, ...

więc funkcja IMO powinna brzmieć:

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

i aby zawinąć go w tablicę danych (jak się powszechnie oczekuje), a na koniec wysłać jako ciąg aplikacji App.stringify ({data: App.toJson ('#cropform: input')})

Aby zapoznać się ze strantyfikacją, patrz pytanie 3593046 dla wersji lean, json2.js dla wersji na każdą ewentualność. To wszystko powinno obejmować :)

Frank Nocke
źródło
Dziękuję .. to sprawia (jak wspomniałeś) drobną, ale bardzo ważną różnicę.
Adarsha
2

Aby uzyskać szybkie, nowoczesne rozwiązanie, użyj wtyczki JSONify jQuery. Poniższy przykład został zaczerpnięty dosłownie z GITHub README. Podziękowania dla Kushal Pandya, autora wtyczki.

Dany:

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

Bieganie:

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

Produkuje:

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

Jeśli chcesz wykonać test POST jQuery z tym obiektem JSON:

$('#mybutton').click(function() {
    $.post('/api/user', JSON.stringify($('#myform').jsonify()));
}
Jim Stewart
źródło