Đoạn đầu tiên là dành cho Rails 3.1 (các phiên bản cũ hơn sẽ khá giống nhau); đoạn thứ hai là dành cho JSON không chuẩn Rails. Chuyển đến cuối nếu tl; dr.
Vấn đề của bạn là Rails thực hiện điều này:
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
klass.class_eval <<-RUBY, __FILE__, __LINE__
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
RUBY
end
trong active_support/core_ext/object/to_json.rb
. Đặc biệt, nó thay đổi phương thức to_json
của Hash chỉ thành một cuộc gọi ActiveSupport::JSON.encode
.
Sau đó, nhìn vào ActiveSupport::JSON::Encoding::Encoder
, chúng tôi thấy điều này:
def encode(value, use_options = true)
check_for_circular_references(value) do
jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
jsonified.encode_json(self)
end
end
Vì vậy, tất cả các mã hóa Rails JSON đi qua as_json
. Tuy nhiên, bạn không xác định as_json
cho Set của riêng mình, bạn chỉ cần thiết lập to_json
và bị nhầm lẫn khi Rails bỏ qua thứ gì đó mà nó không sử dụng.
Nếu bạn thiết lập của riêng bạn Set#as_json
:
class Set
def as_json(options = { })
{
"json_class" => self.class.name,
"data" => { "elements" => self.to_a }
}
end
end
sau đó bạn sẽ có được những gì bạn đang sau trong Rails console và Rails nói chung:
> require 'set'
> s = Set.new([1,2,3])
> s.to_json
=> "{\"json_class\":\"Set\",\"data\":{\"elements\":[1,2,3]}}"
> h = { :set => s }
> h.to_json
=> "{\"set\":{\"json_class\":\"Set\",\"data\":{\"elements\":[1,2,3]}}}"
Hãy ghi nhớ rằng as_json
được sử dụng để chuẩn bị một đối tượng để tuần tự hóa JSON và sau đó to_json
tạo ra chuỗi JSON thực tế. Các phương thức as_json
thường trả về các cấu trúc dữ liệu tuần tự đơn giản, chẳng hạn như Hash và Array, và có các tương tự trực tiếp trong JSON; sau đó, khi bạn có thứ gì đó được cấu trúc như JSON, to_json
được sử dụng để tuần tự hóa nó thành một chuỗi JSON tuyến tính.
Khi chúng ta nhìn vào các phi Rails thư viện JSON tiêu chuẩn, chúng ta thấy những thứ như thế này:
def to_json(*a)
as_json.to_json(*a)
end
khỉ vá vào các lớp học cơ bản (Symbol, Time, Date, ...).Vì vậy, một lần nữa, to_json
thường được triển khai theo điều khoản của as_json
. Trong môi trường này, chúng ta cần phải bao gồm các tiêu chuẩn to_json
cũng như trên as_json
cho Set:
class Set
def as_json(options = { })
{
"json_class" => self.class.name,
"data" => { "elements" => self.to_a }
}
end
def to_json(*a)
as_json.to_json(*a)
end
def self.json_create(o)
new o["data"]["elements"]
end
end
Và chúng tôi bao gồm phương pháp json_create
lớp học của bạn cho bộ giải mã. Khi đã tất cả các thiết lập đúng, chúng ta có được những điều như thế này trong irb
:
>> s = Set.new([1,2,3])
>> s.as_json
=> {"json_class"=>"Set", "data"=>{"elements"=>[1, 2, 3]}}
>> h = { :set => s }
>> h.to_json
=> "{"set":{"json_class":"Set","data":{"elements":[1,2,3]}}}"
Tóm tắt: Nếu bạn đang ở trong Rails, đừng lo lắng về việc làm bất cứ điều gì với to_json
, as_json
là gì bạn muốn chơi với. Nếu bạn không ở trong Rails, hãy thực hiện hầu hết lôgic của bạn trong as_json
(bất kể tài liệu nói gì) và thêm thực hiện tiêu chuẩn to_json
(def to_json(*a);as_json.to_json(*a);end
).
Bạn đang sử dụng phiên bản Ruby nào? Cố gắng này trong 1.8.7 và nó làm việc như bạn muốn. Có thể bạn đang làm điều gì khác ngăn cản hành vi đó. – RocketR