Làm cách nào để ghi đè to_json trong Rails?


94

Cập nhật:

Vấn đề này đã không được khám phá đúng cách. Vấn đề thực sự nằm ở bên trong render :json.

Việc dán mã đầu tiên vào câu hỏi ban đầu sẽ mang lại kết quả như mong đợi. Tuy nhiên, vẫn có một sự báo trước. Xem ví dụ này:

render :json => current_user

KHÔNG giống như

render :json => current_user.to_json

Nghĩa là, render :jsonsẽ không tự động gọi to_jsonphương thức được liên kết với đối tượng Người dùng. Trong thực tế , nếu to_jsonđang được ghi đè trên Usermô hình, render :json => @usersẽ tạo ra ArgumentErrormô tả bên dưới.

tóm lược

# works if User#to_json is not overridden
render :json => current_user

# If User#to_json is overridden, User requires explicit call
render :json => current_user.to_json

Tất cả điều này có vẻ ngớ ngẩn đối với tôi. Điều này dường như cho tôi biết rằng renderkhông thực sự gọi Model#to_jsonkhi loại :jsonđược chỉ định. Ai đó có thể giải thích những gì thực sự đang xảy ra ở đây?

Bất kỳ genii nào có thể giúp tôi điều này đều có thể trả lời câu hỏi khác của tôi: Cách tạo phản hồi JSON bằng cách kết hợp @ foo.to_json (tùy chọn) và @ bar.to_json (tùy chọn) trong Rails


Câu hỏi gốc:

Tôi đã thấy một số ví dụ khác trên SO, nhưng tôi không làm được những gì tôi đang tìm kiếm.

Tôi đang cô:

class User < ActiveRecord::Base

  # this actually works! (see update summary above)
  def to_json
    super(:only => :username, :methods => [:foo, :bar])
  end

end

Tôi đang ArgumentError: wrong number of arguments (1 for 0)vào

/usr/lib/ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json

Bất kỳ ý tưởng?


Ví dụ của bạn hoạt động trong một trong những mô hình của tôi. Làm bất cứ username, foohoặc barphương thức yêu cầu đối số?
Jonathan Julian

Không, usernamekhông phải là một phương pháp và foobarkhông đòi hỏi phương pháp. Tôi đã cập nhật câu hỏi của mình để cho biết lỗi đang xảy ra ở đâu.
maček

Tôi đang chạy 1.8.7. Bạn sẽ phải mở tệp đó và xem lý do tại sao nó chuyển một đối số đến một phương thức mong đợi không có args.
Jonathan Julian

Câu trả lời:


214

Bạn đang nhận được ArgumentError: wrong number of arguments (1 for 0)to_jsoncần phải được ghi đè bằng một tham số, optionshàm băm.

def to_json(options)
  ...
end

Giải thích dài hơn về to_json,as_json và dựng hình:

Trong ActiveSupport 2.3.3, as_jsonđã được thêm vào để giải quyết các vấn đề như bạn đã gặp phải. Việc tạo json nên tách biệt với kết xuất json.

Bây giờ, bất cứ lúc nào to_jsonđược gọi trên một đối tượng, as_jsonđược gọi để tạo cấu trúc dữ liệu và sau đó hàm băm đó được mã hóa dưới dạng chuỗi JSON bằng cách sử dụngActiveSupport::json.encode . Điều này xảy ra cho tất cả các loại: đối tượng, số, ngày, chuỗi, v.v. (xem mã ActiveSupport).

Các đối tượng ActiveRecord hoạt động theo cùng một cách. Có một as_jsontriển khai mặc định tạo ra một hàm băm bao gồm tất cả các thuộc tính của mô hình. Bạn nên ghi đè as_jsontrong Mô hình của mình để tạo cấu trúc JSON mà bạn muốn . as_json, giống như phiên bản cũ to_json, sử dụng hàm băm tùy chọn nơi bạn có thể chỉ định các thuộc tính và phương thức để đưa vào một cách khai báo.

def as_json(options)
  # this example ignores the user's options
  super(:only => [:email, :handle])
end

Trong bộ điều khiển của bạn, render :json => ocó thể chấp nhận một chuỗi hoặc một đối tượng. Nếu đó là một chuỗi, nó được chuyển qua dưới dạng phần thân phản hồi, nếu là một đối tượng, to_jsonsẽ được gọi, kích hoạt as_jsonnhư đã giải thích ở trên.

Vì vậy, miễn là các mô hình của bạn được thể hiện đúng với các as_jsonghi đè (hoặc không), mã bộ điều khiển của bạn để hiển thị một mô hình sẽ trông như sau:

format.json { render :json => @user }

Đạo lý của câu chuyện là: Tránh gọi điện to_jsontrực tiếp, cho phép renderlàm điều đó cho bạn. Nếu bạn cần điều chỉnh đầu ra JSON, hãy gọi as_json.

format.json { render :json => 
    @user.as_json(:only => [:username], :methods => [:avatar]) }

@Jonathan Julian, đây là lời giải thích rất hữu ích về as_json. Như bạn có thể thấy trong tài liệu ActiveRecord :: Serialization ( api.rubyonrails.org/classes/ActiveRecord/… ), có rất ít (không) tài liệu cho việc này. Tôi sẽ thử cái này :)
maček

1
@Jonathan Julian, nếu tôi có thể bỏ phiếu 10 lần này, tôi sẽ. Tài as_jsonliệu đâu rồi! Cảm ơn một lần nữa :)
maček

71

Nếu bạn gặp sự cố với điều này trong Rails 3, hãy ghi đè serializable_hashthay vì as_json. Điều này cũng sẽ nhận được định dạng XML của bạn miễn phí :)

Điều này khiến tôi mãi mãi không tìm ra. Hy vọng rằng sẽ giúp một ai đó.


1
Có ai biết về bất kỳ bài viết tốt về phương pháp serializable_hash? Khi tôi sử dụng nó, nó thay đổi sản lượng xml tiếp theo của tôi từ gói các đối tượng với tên gọi của nó (ví dụ: "quote" cho một đối tượng quote ") để thay luôn quấn nó với '<băm>'.
Tyler Collier

@TylerCollier nó phải cùng tùy chọn nhưto_xml
Sam Soffes

Cảm ơn cho giải pháp này! Tôi đang sử dụng ruby2 / rails4 và as_json không hoạt động với các đối tượng lồng nhau, phương thức ghi đè không được gọi trong 'include', với serializable_hash, nó hoạt động!
santuxus

Hãy xem robots.thoughtbot.com/better-serialization-less-as-json để biết giải thích tại sao serializable_hash nên được ghi đè.
Topher Hunt

36

Đối với những người không muốn bỏ qua các tùy chọn của người dùng nhưng cũng thêm của họ:

def as_json(options)
  # this example DOES NOT ignore the user's options
  super({:only => [:email, :handle]}.merge(options))
end

Hy vọng điều này sẽ giúp bất cứ ai :)


1
Đây là cách tôi làm điều đó, ngoại trừ tôi mặc định các optionshash với = {}vì vậy nó không cần thiết khi gọi
mroach

4

Ghi đè không phải to_json mà là as_json. Và từ as_json, hãy gọi những gì bạn muốn:

Thử cái này:

def as_json 
 { :username => username, :foo => foo, :bar => bar }
end

Không as_jsonchỉ dành cho ActiveResource?
Jonathan Julian

Rõ ràng ActiveRecord :: Serialization có as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
glebm

@glebm, tôi đã thử cách này và nhận được kết quả tương tự. Tôi đã cập nhật câu hỏi của tôi để cho bạn thấy.
maček

@glebm, tôi vẫn gặp lỗi tương tự. Ngay cả khi tôi thực hiện, render :json => current_usertôi vẫn nhận được kết quả mặc định như mong đợi (tất cả các thuộc tính cho Usermô hình ở định dạng JSON). Khi tôi thêm các as_jsonphương pháp để tôi Usermô hình và thử cùng một điều, tôi nhận được lỗi :(
Macek

@glebm, cảm ơn bạn. Tôi biết mình đã làm sai. Nó có thể đáng để kiểm tra câu hỏi cập nhật.
maček
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.