Rails неправильно декодирует JSON из jQuery (массив становится хешем с целочисленными ключами) - PullRequest
87 голосов
/ 20 июня 2011

Каждый раз, когда я хочу разместить массив объектов JSON с jQuery в Rails, у меня возникает эта проблема.Если я упорядочиваю массив, я вижу, что jQuery работает правильно:

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

Но если я просто отправлю массив в качестве данных вызова AJAX, я получу:

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

Принимая во внимание, что если я просто отправляю простой массив, он работает:

"shared_items"=>["entity_253"]

Почему Rails меняет массив на этот странный хеш?Единственная причина, которая приходит на ум, заключается в том, что Rails не может правильно понять содержимое, потому что здесь нет типа (есть ли способ установить его в вызове jQuery?):

Processing by SharedListsController#create as 

Спасибо!

Обновление: Я отправляю данные в виде массива, а не строки, и массив создается динамически с использованием функции .push().Пробовал с $.post и $.ajax, тот же результат.

Ответы [ 7 ]

163 голосов
/ 28 июля 2012

Если кто-то наткнется на это и захочет найти лучшее решение, вы можете указать опцию "contentType: 'application / json'" в вызове .ajax и заставить Rails правильно анализировать объект JSON, не разбирая его в хеши с целочисленными ключами со строковыми значениями.

Итак, подведем итог, моя проблема заключалась в том, что это:

$.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 разбирает вещи как:

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

тогда как это (ПРИМЕЧАНИЕ: теперь мы строковые данные javascript-объекта и указываем тип контента, поэтому rails будет знать, как анализировать нашу строку):

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

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

Это работает для меня в Rails 3, на Ruby 1.9.3.

11 голосов
/ 11 августа 2011

Немного старый вопрос, но я боролся с этим сам сегодня, и вот ответ, который я придумал: я считаю, что это слегка вина jQuery, но он делает только то, что естественно для него.У меня, однако, есть обходной путь.

Учитывая следующий ajax-вызов jQuery:

$.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, будут выглядеть примерно так (если вы посмотрите на Запрос в вашемFirebug-of-Choice) даст вам данные формы, которые выглядят следующим образом:

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

Если вы получите CGI.unencode, который вы получите

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

Я полагаю, это потому, что JQuery думаетчто эти ключи в вашем JSON являются именами элементов формы, и что он должен обращаться с ними так, как если бы у вас было поле с именем «user [name]».

Таким образом, они входят в ваше приложение Rails, Rails видит скобки,и создает хеш для хранения самого внутреннего ключа имени поля («1», который jQuery «услужливо» добавил).

В любом случае, я справился с этим поведением, сконструировав свой вызов ajax следующим образом;

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

Что заставляет jQuery думать, что этот JSON представляет собой значение , которое вы хотите передать целиком, а не объект Javascript, который он должен принять, и повернуть всеключи в имена полей формы.

However, это означает, что на стороне Rails все немного по-другому, потому что вам нужно явно декодировать JSON в params [: data].

Но это нормально:

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

TL; DR: Итак, решение заключается в следующем: в параметре data для вашего вызова jQuery.ajax (), сделайте явно {"data": JSON.stringify(my_object) } вместо подачи массива JSON в jQuery (где он ошибочно угадывает, что вы хотитеделать с этим.

6 голосов
/ 25 августа 2014

Я только что столкнулся с этой проблемой в Rails 4. Чтобы четко ответить на ваш вопрос («Почему Rails меняет массив на этот странный хэш?»), См. Раздел 4.1 руководства Rails по контроллерам действий :

Чтобы отправить массив значений, добавьте пустую пару квадратных скобок «[]» к имени ключа.

Проблема в том, что jQuery форматирует запрос с явными индексами массива, а не с пустыми квадратными скобками. Так, например, вместо отправки shared_items[]=1&shared_items[]=2 он отправляет shared_items[0]=1&shared_items[1]=2. Rails видит индексы массива и интерпретирует их как ключи хеша, а не индексы массива, превращая запрос в странный хэш Ruby: { shared_items: { '0' => '1', '1' => '2' } }.

Если у вас нет контроля над клиентом, вы можете исправить эту проблему на стороне сервера, преобразовав хеш в массив. Вот как я это сделал:

shared_items = []
params[:shared_items].each { |k, v|
  shared_items << v
}
1 голос
/ 04 ноября 2014

следующий метод может быть полезен, если вы используете сильные параметры

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 голосов
/ 19 января 2015

Используйте rack-jquery-params gem (заявление об отказе: я автор). Это исправляет проблему, когда массивы становятся хешами с целочисленными ключами.

0 голосов
/ 20 июня 2011

Вы просто пытаетесь вставить строку JSON в действие контроллера Rails?

Я не уверен, что Rails делает с хешем, но вы можете обойти проблему и получить больше удачи, создав объект Javascript / JSON (в отличие от строки JSON) и отправив его через параметр данных для вашего вызова Ajax.

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

Если вы хотите отправить это через ajax, вы должны сделать что-то вроде этого:

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

Обратите внимание, что с помощью приведенного выше фрагмента ajax jQuery сделает интеллектуальное предположение о dataType, хотя обычно лучше указать его явно.

В любом случае, в вашем действии контроллера вы можете получить объект JSON, который вы передали с хэшем params, т.е.

params[:shared_items]

например. это действие вернет вам объект json:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end
0 голосов
/ 20 июня 2011

Рассматривали ли вы делать parsed_json = ActiveSupport::JSON.decode(your_json_string)? Если вы отправляете что-то другое, вы можете использовать .to_json для сериализации данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...