Raylar, jQuery'den JSON kodunu doğru bir şekilde çözmüyor (dizi, tamsayı anahtarları ile bir karma haline geliyor)


89

JQuery to Rails ile JSON nesnelerinin bir dizisini POST yapmak istediğimde, bu sorunu yaşıyorum. Diziyi dizgeleştirirsem, jQuery'nin işini doğru yaptığını görebilirim:

"shared_items"=>"[{\"entity_id\":\"253\",\"position\":1},{\"entity_id\":\"823\",\"position\":2}]"

Ama diziyi AJAX çağrısının verisi olarak gönderirsem şunu elde ederim:

"shared_items"=>{"0"=>{"entity_id"=>"253", "position"=>"1"}, "1"=>{"entity_id"=>"823", "position"=>"2"}}

Oysa düz bir dizi gönderirsem işe yarar:

"shared_items"=>["entity_253"]

Rails neden diziyi bu tuhaf hash ile değiştiriyor? Akla gelen tek neden, Rails'in içeriği doğru bir şekilde anlayamamasıdır çünkü burada tip yoktur (jQuery çağrısında ayarlamanın bir yolu var mı?):

Processing by SharedListsController#create as 

Teşekkür ederim!

Güncelleme: Verileri bir dizi olarak gönderiyorum, bir dize olarak değil ve dizi, .push()işlev kullanılarak dinamik olarak oluşturuldu . İle çalıştı $.postve $.ajaxaynı sonucu.

Yanıtlar:


169

Birinin buna rastlaması ve daha iyi bir çözüm istemesi durumunda, .ajax çağrısında "contentType: 'application / json'" seçeneğini belirtebilir ve Rails'in JSON nesnesini all-key ile tamsayı anahtarlı hashlere dönüştürmeden düzgün bir şekilde ayrıştırmasını sağlayabilirsiniz. dize değerleri.

Yani özetlemek gerekirse, benim sorunum şuydu:

$.ajax({
  type : "POST",
  url :  'http://localhost:3001/plugin/bulk_import/',
  dataType: 'json',
  data : {"shared_items": [{"entity_id":"253","position":1}, {"entity_id":"823","position":2}]}
});

Rails'in şeyleri şu şekilde ayrıştırmasına neden oldu:

Parameters: {"shared_items"=>{"0"=>{"entity_id"=>"253", "position"=>"1"}, "1"=>{"entity_id"=>"823", "position"=>"2"}}}

oysa bu (NOT: şimdi javascript nesnesini diziliyoruz ve bir içerik türü belirliyoruz, böylece raylar dizimizi nasıl ayrıştıracağını bilecek):

$.ajax({
  type : "POST",
  url :  'http://localhost:3001/plugin/bulk_import/',
  dataType: 'json',
  contentType: 'application/json',
  data : JSON.stringify({"shared_items": [{"entity_id":"253","position":1}, {"entity_id":"823","position":2}]})
});

Rails'te güzel bir nesne ile sonuçlanır:

Parameters: {"shared_items"=>[{"entity_id"=>"253", "position"=>1}, {"entity_id"=>"823", "position"=>2}]}

Bu benim için Ruby 1.9.3'te Rails 3'te çalışıyor.


1
JQuery'den JSON almanın Rails uygulamaları için oldukça yaygın bir durum olduğu düşünüldüğünde, bu Rails için doğru davranış gibi görünmüyor. Rails'in neden bu şekilde davrandığına dair savunulabilir bir mantık var mı? Görünüşe göre istemci tarafı JS kodu gereksiz yere çemberlerden geçmek zorunda kalıyor.
Jeremy Burton

1
Sanırım yukarıdaki davada suçlu jQuery. iirc jQuery ayarı değildi Content-Typeiçin isteğin application/json, ancak bunun yerine bir html formu olduğu gibi veri gönderirken? Bu kadar geriye doğru düşünmek zor. Rails, Content-Typedoğru değere ayarlandıktan sonra gayet iyi çalışıyordu ve gönderilen veriler geçerli JSON idi.
swajak

3
Sadece dikkat istiyor nesneyi stringify aynı zamanda belirtmek @swajak yaptılarcontentType: 'application/json'
Roger Lam

1
Teşekkürler @RogerLam, umduğumu daha iyi açıklamak için çözümü güncelledim
swajak

1
@swajak: çok teşekkür ederim! Gerçekten günümü kurtardın! :)
Kulgar

12

Biraz eski bir soru, ama bugün bununla kendim mücadele ettim ve işte bulduğum cevap: Bunun biraz jQuery'nin hatası olduğuna inanıyorum, ama sadece doğal olanı yapmak. Bununla birlikte, bir çözümüm var.

Aşağıdaki jQuery ajax çağrısı verildiğinde:

$.ajax({
   type : "POST",
   url :  'http://localhost:3001/plugin/bulk_import/',
   dataType: 'json',
   data : {"shared_items": [{"entity_id":"253","position":1},{"entity_id":"823","position":2}]}

});

JQuery'nin göndereceği değerler şuna benzer şekilde görünecektir (Firebug-tercihinizdeki İsteğe bakarsanız) size aşağıdaki gibi görünen form verilerini verecektir:

shared_items%5B0%5D%5Bentity_id%5D:1
shared_items%5B0%5D%5Bposition%5D:1

Eğer alacağınız CGI.unencode

shared_items[0][entity_id]:1
shared_items[0][position]:1

Bunun nedeni, jQuery'nin JSON'nizdeki bu anahtarların form öğesi adları olduğunu ve onlara "kullanıcı [ad]" adlı bir alanınız varmış gibi davranması gerektiğini düşünmesinden kaynaklandığına inanıyorum.

Böylece, Rails uygulamanıza gelirler, Rails parantezleri görür ve alan adının en içteki anahtarını tutmak için bir karma oluşturur (jQuery'nin "yararlı" eklediği "1").

Her neyse, ajax çağrımı şu şekilde oluşturarak bu davranışı aştım;

$.ajax({
   type : "POST",
   url :  'http://localhost:3001/plugin/bulk_import/',
   dataType: 'json',
   data : {"data": JSON.stringify({"shared_items": [{"entity_id":"253","position":1},{"entity_id":"823","position":2}])},
  }
});

Hangi güçler jQuery bu JSON bir olduğunu düşünmek değer Eğer tamamen ve geçmek istediğiniz değil alması gereken bir Javascript nesnesi ve tüm anahtarları form alanı adlarına dönüştürür.

Bununla birlikte, bu, Rails tarafında işlerin biraz farklı olduğu anlamına gelir, çünkü JSON'un şifresini params [: data] içinde açıkça çözmeniz gerekir.

Ama sorun değil:

ActiveSupport::JSON.decode( params[:data] )

TL; DR: Yani çözüm şudur: jQuery.ajax () çağrınızın data parametresinde {"data": JSON.stringify(my_object) }, JSON dizisini jQuery'ye beslemek yerine açıkça yapın (burada, onunla ne yapmak istediğinizi yanlış tahmin eder.


Daha iyi bir çözüm aşağıdadır @swajak.
event_jr

7

Bu sorunla az önce Rails 4 ile karşılaştım. Sorunuzu açıkça yanıtlamak için ("Rails neden diziyi bu tuhaf hash ile değiştiriyor?"), Eylem Denetleyicileri ile ilgili Rails kılavuzunun 4.1 bölümüne bakın :

Bir değer dizisi göndermek için, anahtar adına boş bir çift köşeli parantez "[]" ekleyin.

Sorun, jQuery'nin isteği boş köşeli parantezler yerine açık dizi indeksleriyle biçimlendirmesidir. Yani mesela göndermek yerine shared_items[]=1&shared_items[]=2gönderiyor shared_items[0]=1&shared_items[1]=2. Bir garip Ruby karma haline isteği dönüm, dizi indeksleri ve karma tuşları yerine dizi indisleri olarak, yorumlayan onları görür Raylar: { shared_items: { '0' => '1', '1' => '2' } }.

İstemcinin kontrolüne sahip değilseniz, karmayı bir diziye dönüştürerek sunucu tarafında bu sorunu çözebilirsiniz. İşte böyle yaptım:

shared_items = []
params[:shared_items].each { |k, v|
  shared_items << v
}

1

Güçlü parametreler kullanırsanız aşağıdaki yöntem yardımcı olabilir

def safe_params
  values = params.require(:shared_items)
  values = items.values if items.keys.first == '0'
  ActionController::Parameters.new(shared_items: values).permit(shared_items: [:entity_id, :position]).require(:shared_items)
end

0

Yapmayı düşündün parsed_json = ActiveSupport::JSON.decode(your_json_string)mü Başka bir yolla gönderiyorsanız .to_json, verileri serileştirmek için kullanabilirsiniz .


1
Evet dikkate alındı, ancak bunu manuel olarak kodlamak / kod çözmek zorunda kalmadan Rails'te yapmanın "doğru bir yolu" olup olmadığını bilmek istedim.
aledalgrande

0

JSON dizesini bir Rails denetleyici eylemine mi sokmaya çalışıyorsunuz?

Rails'in hash ile ne yaptığından emin değilim, ancak bir Javascript / JSON nesnesi (JSON dizesinin aksine) oluşturarak ve bunu Ajax'ınız için veri parametresi olarak göndererek sorunu aşabilir ve daha fazla şansa sahip olabilirsiniz. aramak.

myData = {
  "shared_items":
    [
        {
            "entity_id": "253",
            "position": 1
        }, 
        {
            "entity_id": "823",
            "position": 2
        }
    ]
  };

Bunu ajax aracılığıyla göndermek istersen, şöyle bir şey yaparsın:

$.ajax({
    type: "POST",
    url: "my_url",    // be sure to set this in your routes.rb
    data: myData,
    success: function(data) {          
        console.log("success. data:");
        console.log(data);
    }
});

Yukarıdaki ajax parçacığı ile, jQuery'nin dataType hakkında akıllıca bir tahmin yapacağını unutmayın, ancak bunu açıkça belirtmek genellikle iyidir.

Her iki durumda da, denetleyici eyleminizde, geçtiğiniz JSON nesnesini params hash ile alabilirsiniz, yani

params[:shared_items]

Örneğin, bu eylem json nesnenizi size geri gönderecektir:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

Teşekkürler, ama şu anda yaptığım şey bu (güncellemeye bakın) ve işe yaramıyor.
aledalgrande

0

Kullanım raf jquery-params mücevher (yasal uyarı: Ben yazarım). Tamsayı anahtarları ile dizilerin karma hale gelmesi sorununuzu çözer.


Bağlantı koymak yerine kod pasajı sağlamak daha
iyidir
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.