Rails не декодирует JSON из jQuery правильно (массив становится хэшем с целочисленными ключами)


каждый раз, когда я хочу опубликовать массив объектов 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 меняет массив на этот странный хэш? Единственная причина, которая приходит на ум, это то, что рельсы не могут правильно понять содержимое, потому что здесь нет типа (есть ли способ установить его в вызове jQuery?):

Processing by SharedListsController#create as 

спасибо!

обновление: Я отправляю данные в виде массива, а не строки, и массив создается динамически с помощью

7 82

7 ответов:

в случае, если кто-то натыкается на это и хочет лучшего решения, вы можете указать опцию "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.

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

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

$.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 он должен взять и превратить все ключи в имена полей формы.

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

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

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

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

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

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

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

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

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

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

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

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

вы просто пытаетесь получить строку 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 будет сделать правильный выбор на тип данных, хотя это, как правило, хорошо, чтобы указать это явно.

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

params[:shared_items]

например, это действие выплюнет ваш объект json обратно на вас:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

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