RailsはjQueryからJSONを正しくデコードしていません(配列は整数キーのハッシュになります)


89

JSONオブジェクトの配列をjQuery to RailsでPOSTするたびに、この問題が発生します。配列を文字列化すると、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、同じ結果。

回答:


169

誰かがこれに遭遇し、より良い解決策が必要な場合は、.ajax呼び出しで「contentType: 'application / json'」オプションを指定し、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オブジェクトを文字列化してコンテンツタイプを指定しているので、レールは文字列を解析する方法を知っています):

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

これは、Ruby 1.9.3上のRails 3で動作します。


1
JQueryからのJSONの受信はRailsアプリのかなり一般的なケースであるため、これはRailsの正しい動作のようには見えません。Railsがこのように動作する理由について、弁護できる根拠はありますか?クライアント側のJSコードは不必要にフープを飛び越えなければならないようです。
ジェレミーバートン

1
上記の場合、原因はjQueryだと思います。iirc jQueryはContent-Typeリクエストのをに設定していませんでしapplication/jsonたが、代わりにHTMLフォームとしてデータを送信していましたか?ここまで考えるのは難しい。がContent-Type正しい値に設定された後、Railsは問題なく機能し、送信されるデータは有効なJSONでした。
スワハク2014年

3
@swajakがオブジェクトを文字列化しただけでなく、指定したことにも注意したいcontentType: 'application/json'
Roger Lam

1
@RogerLamに感謝します。解決策を更新して、私がよりよく望むことを説明しました。
スワジャク

1
@swajak:どうもありがとうございました!あなたは本当に私の日を救った!:)
Kulgar

12

少し古い質問ですが、私は今日自分自身でこれと戦いました。ここで私が思いついた答えは次のとおりです。これは少し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が角かっこを見て、フィールド名の最も内側のキー(jQueryが「効果的に」追加した「1」)を保持するハッシュを作成します。

とにかく、私は私の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側では少し異なります。params[:data]でJSONを明示的にデコードする必要があるためです。

しかし、それは問題ありません:

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

TL; DR:したがって、解決策は次のとおりです。jQuery.ajax()呼び出しのデータパラメーターで{"data": JSON.stringify(my_object) }、JSON配列をjQueryにフィードするのではなく、明示的に行います(そこで何をしたいかを誤って推測します)。


ヘッズアップのより良いソリューションは、@ swajakの下にあります。
event_jr

7

Rails 4でこの問題に遭遇しました。質問に明示的に答えるには(「Railsが配列をその奇妙なハッシュに変更するのはなぜですか?」)、Action Controllerに関するRailsガイドのセクション4.1を参照してください。

値の配列を送信するには、角括弧の空のペア「[]」をキー名に追加します。

問題は、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

次の方法は、強力なパラメーターを使用する場合に役立ちます

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

あなたはそうすることを考えましたparsed_json = ActiveSupport::JSON.decode(your_json_string)か?他の方法で.to_jsonデータを送信する場合は、データのシリアル化に使用できます。


1
はい検討しましたが、Railsでこれを行う「正しい方法」があり、手動でエンコード/デコードする必要がないかどうかを知りたいと思っていました。
aledalgrande 2011年

0

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をインテリジェントに推測しますが、通常は明示的に指定することをお勧めします。

どちらの方法でも、コントローラーアクションで、paramsハッシュを使用して渡したJSONオブジェクトを取得できます。

params[:shared_items]

たとえば、このアクションはjsonオブジェクトを吐き出します:

def reply_in_json
  @shared = params[:shared_items]

  render :json => @shared
end

ありがとう、しかしそれは私が今やっていることです(更新を参照)そしてそれは機能しません。
aledalgrande 2011年

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.