Rails가 jQuery에서 JSON을 올바르게 디코딩하지 않음 (정수 키가있는 해시가되는 배열)


89

jQuery를 사용하여 JSON 객체 배열을 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, 동일한 결과를 시도했습니다 .

답변:


169

누군가이 문제를 발견하고 더 나은 솔루션을 원하는 경우 .ajax 호출에서 "contentType : 'application / json'"옵션을 지정하고 Rails가 JSON 객체를 all- 정수 키가있는 해시로 분해하지 않고 올바르게 구문 분석하도록 할 수 있습니다. 문자열 값.

요약하면 내 문제는 다음과 같습니다.

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

(참고 : 이제 자바 스크립트 객체를 문자열 화하고 콘텐츠 유형을 지정하고 있으므로 레일은 문자열을 구문 분석하는 방법을 알게됩니다.)

$.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 코드가 불필요하게 후프를 통과해야하는 것 같습니다.
Jeremy Burton

1
위의 경우 범인은 jQuery라고 생각합니다. iirc jQuery는 Content-Type요청을로 설정하지 application/json않았지만 대신 html 형식으로 데이터를 보내고 있습니까? 지금까지 생각하기 어렵습니다. Rails는를 Content-Type올바른 값으로 설정하고 전송되는 데이터가 유효한 JSON 인 후에 제대로 작동했습니다 .
swajak

3
@swajak가 객체를 문자열 화했을뿐만 아니라 명시하고 싶을뿐만 아니라contentType: 'application/json'
Roger Lam

1
감사 @RogerLam, 내가 더 잘 나는 희망을 설명하는 솔루션을 업데이트 한
swajak

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에서 Request를 보면)는 다음과 같은 양식 데이터를 제공합니다.

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이라고 생각하는 가치 는 완전히 통과 할 것인지, 그리고 하지 가 걸릴 및 양식 필드 이름에 모든 키를 설정해야합니다 자바 스크립트 객체.

그러나 이는 params [: data]에서 JSON을 명시 적으로 디코딩해야하기 때문에 Rails 측에서는 상황이 약간 다르다는 것을 의미합니다.

하지만 괜찮습니다.

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

TL; DR : 따라서 해결책은 다음과 같습니다. jQuery.ajax () 호출에 대한 데이터 매개 변수 {"data": JSON.stringify(my_object) }에서 JSON 배열을 jQuery에 입력하는 대신 명시 적으로 수행합니다 (여기서 수행하려는 작업을 잘못 추측하는 경우).


더 나은 해결책은 @swajak의 아래입니다.
event_jr 2013

7

방금 Rails 4에서이 문제가 발생했습니다. 귀하의 질문에 명시 적으로 답변하려면 ( "Rails가 배열을 이상한 해시로 변경하는 이유는 무엇입니까?"), Action Controllers에 대한 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-06-20

0

Rails 컨트롤러 작업에 JSON 문자열을 가져 오려고합니까?

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

0

rack-jquery-params gem을 사용하세요 (면책 조항 : 저자입니다). 배열이 정수 키로 해시되는 문제를 해결합니다.


링크를 넣는 대신 코드 스 니펫을 제공하는 것이 더 좋습니다
Vikasdeep Singh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.