2011-07-29 10 views
9

私は、Ruby標準ライブラリのSetクラスを使用するデータ構造を持っています。自分のデータ構造をJSON文字列にシリアル化できるようにしたいと思います。デフォルトではネストされたオブジェクトでカスタムto_jsonメソッドを使用する

は、セットが配列としてシリアライズ:あなたはそれをデシリアライズしようとまでは結構です

>> s = Set.new [1,2,3] 
>> s.to_json 
=> "[1,2,3]" 

を。

>> a = { 'set' => s } 
>> a.to_json 
=> "{\"set\":[1,2,3]}" 

任意のアイデア:私はハッシュか何かにセットを置くまで

>> s = Set.new [1,2,3] 
>> s.to_json 
=> "{\"data\":{\"elements\":[1,2,3]},\"json_class\":\"Set\"}" 

class Set 
    def to_json(*a) 
    { 
     "json_class" => self.class.name, 
     "data" => { 
     "elements" => self.to_a 
     } 
    }.to_json(*a) 
    end 

    def self.json_create(o) 
    new o["data"]["elements"] 
    end 
end 

素晴らしい作品:

は、だから私は、カスタムto_jsonメソッドを定義しなぜ私のカスタムto_jsonは、Setが別のobjの中に入れ子になっても呼び出されないect?

+0

使用しているRubyのバージョンはどれですか? 1.8.7でこれを試してみました。たぶんあなたはそのような行動を妨げる何か他のことをしています。 – RocketR

答えて

20

最初のチャンクはRails 3.1用です(古いバージョンはほぼ同じです)。 2番目のチャンクは標準の非Rails JSON向けです。 tl; drならば最後にスキップしてください。


あなたの問題は、Railsがこのないことである:active_support/core_ext/object/to_json.rb

[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 

。特に、これはHashのto_jsonメソッドをActiveSupport::JSON.encode呼び出しに変更します。

はその後、ActiveSupport::JSON::Encoding::Encoderを見て、我々はこれを参照してください。

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 

だから、すべてのRailsのJSONエンコーディングはas_jsonを通過します。しかし、あなた自身がas_jsonをSetに定義していない場合は、to_jsonを設定するだけで、Railsが使用しないものを無視すると混乱することになります。

あなたがあなた自身のSet#as_jsonを設定した場合:

class Set 
    def as_json(options = { }) 
     { 
      "json_class" => self.class.name, 
      "data" => { "elements" => self.to_a } 
     } 
    end 
end 

を、あなたはあなたが一般的にRailsのコンソールとRailsで後にしているものを取得しますが:

> 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]}}}" 

as_jsonことに注意してくださいJSONシリアル化のオブジェクトを準備するために使用され、次にto_jsonが実際のJSON文字列を生成します。 as_jsonのメソッドは、一般的にハッシュや配列などのシンプルなシリアライズ可能なデータ構造を返し、JSONに直接類似しています。 JSONのような構造になったら、to_jsonを使って線形JSON文字列にシリアル化します。我々は標準的な非RailsのJSONライブラリを見てみると


は、我々はこのようなものを参照してください。

def to_json(*a) 
    as_json.to_json(*a) 
end 

猿は基本クラス(シンボル、時間、日付、...)にパッチ。ですからもう一度to_jsonas_jsonという言葉で実装されています。

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 

そして、我々は、デコーダのためのあなたのjson_createクラスメソッドが含まれます。この環境では、我々はセットのto_json標準と同様にas_json上記を含める必要があります。それがすべて正しくセットアップしたら、我々は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]}}}" 

エグゼクティブサマリー:あなたはRailsのであれば、as_jsonは何か、to_jsonで何も心配しないでくださいあなたは一緒に遊びたいです。 Railsに載っていない場合は、ほとんどのロジックをas_jsonに実装し、標準to_json実装(def to_json(*a);as_json.to_json(*a);end)も追加してください。

0

同じ問題を解決するために、私はRailsの問題追跡ツールでこのバグレポートを見つけました。それは閉鎖されていただけでなく、以前のバージョンではまだ起こっていると思います。それが助けてくれることを願って。ここで

https://github.com/rails/rails/issues/576

1

おそらくto_aメソッドが含まれていないでしょうカスタムクラスのためto_json方法を得るために私のアプローチである(それは最近Objectクラスの実装から削除されました)

ここでちょっとした魔法がありますモジュール内でself.includedを使用します。ここでは、2006年の非常に良い記事moduleインスタンスとクラスの両方のメソッドを持つhttp://blog.jayfields.com/2006/12/ruby-instance-and-class-methods-from.html

このモジュールは、シームレスなto_json機能を提供するためにどのクラスにも含まれるように設計されています。既存のクラスの変更を最小限にするために、独自のメソッドを使用するのではなく、attr_accessorメソッドを傍受します。

module JSONable 
    module ClassMethods 
    attr_accessor :attributes 

    def attr_accessor *attrs 
     self.attributes = Array attrs 
     super 
    end 
    end 

    def self.included(base) 
    base.extend(ClassMethods) 
    end 

    def as_json options = {} 
    serialized = Hash.new 
    self.class.attributes.each do |attribute| 
     serialized[attribute] = self.public_send attribute 
    end 
    serialized 
    end 

    def to_json *a 
    as_json.to_json *a 
    end 
end 


class CustomClass 
    include JSONable 
    attr_accessor :b, :c 

    def initialize b: nil, c: nil 
    self.b, self.c = b, c 
    end 
end 

a = CustomClass.new(b: "q", c: 23) 
puts JSON.pretty_generate a 

{ 
    "b": "q", 
    "c": 23 
} 
関連する問題