20

私は以前持っていました:

serialize :params, JSON

ただし、これによりJSONが返され、ハッシュキーシンボルが文字列に変換されます。ハッシュを操作するときに最も一般的であるように、シンボルを使用してハッシュを参照したいと思います。シンボルをフィードすると、Railsは文字列を返します。これを回避するために、私は独自のゲッター/セッターを作成しました。セッターは十分に単純で(JSONエンコード)、ゲッターは次のとおりです。

  def params
    read_attribute(:params) || JSON.parse(read_attribute(:params).to_json).with_indifferent_access
  end

paramsループが発生するため直接参照できなかったため、を使用しています。これread_attributeで、ハッシュキーを記号または文字列で参照できるようになりました。ただし、これはハッシュを更新しません。

model.params.merge!(test: 'test')
puts model.params # => returns default params without merge

これは、ハッシュがコピーによって参照されていると私に思わせます。

私の質問は2つあります。アクティブレコードのJSONシリアル化を拡張して、無関心なアクセスハッシュを返す(またはシンボルを文字列に変換しない)ことができますか?それでも、マージを使用して上記のようにハッシュを機能させることはできますか?そうでない場合は、ゲッターを改善して機能させるために何ができmodel.params.merge!ますか?

私は(うまくいく)の線に沿って何かを望んでいました:

  def params_merge!(hash)
    write_attribute(:params, read_attribute(:params).merge(hash))
  end

  # usage: model.params_merge!(test: 'test')

さらに良いことに、Railsに無関心なアクセスでハッシュを返すようにするか、シンボルを文字列に変換しないようにしてください。助けに感謝します。

4

4 に答える 4

17

組み込みのserialize方法を使用します。

class Whatever < ActiveRecord::Base
 serialize :params, HashWithIndifferentAccess
end

詳細については、シリアル化に関するActiveRecord::Baseのドキュメントを参照してください。

于 2013-01-28T19:12:14.467 に答える
8

@fguillenの要求に従って、回答としてコメントを投稿します...警告:私は通常、Rubyistではありません...したがって、これは慣用的または効率的ではない可能性があります。機能的には、それは私が欲しかったものを手に入れました。Rails3.2および4.0で動作するようです...

application_helper.rb

module ApplicationHelper
  class JSONWithIndifferentAccess
    def self.load(str)
      obj = HashWithIndifferentAccess.new(JSON.load(str))
      #...or simply: obj = JSON.load(str, nil, symbolize_names:true)
      obj.freeze #i also want it set all or nothing, not piecemeal; ymmv
      obj
    end
    def self.dump(obj)
      JSON.dump(obj)
    end
  end
end

私のモデルには、フィールドrule_specにシリアル化された、というtextフィールドがあります。

serialize :rule_spec, ApplicationHelper::JSONWithIndifferentAccess

最終的には、無関心なアクセスではなく、シンボルが必要であることに気付きましたが、loadメソッドを微調整することで、どちらの動作も得られます。

于 2013-12-19T02:26:40.677 に答える
5

ネストされていない単純なJSONだけでなく、任意JSONで使用できるbimsapiのソリューションのバリエーションを使用することになりました。

これがロードされると...

module JsonHelper

  class JsonWithIndifferentAccess
    def self.load(str)
      self.indifferent_access JSON.load(str)
    end

    def self.dump(obj)
      JSON.dump(obj)
    end

    private

      def self.indifferent_access(obj)
        if obj.is_a? Array
          obj.map!{|o| self.indifferent_access(o)}
        elsif obj.is_a? Hash
          obj.with_indifferent_access
        else
          obj
        end
      end
  end

end

その後、呼び出す代わりに

JSON.load(http_response)

あなたはただ電話します

JsonHelper::JsonWithIndifferentAccess.load(http_response)

同じことを行いますが、ネストされたハッシュはすべて無関心なアクセスです。

大規模なJSONペイロードは、Cで最適化され、パフォーマンスのためにより完全に設計されたネイティブJSONパーサーの上に重要なルビー操作を追加するため、すべての解析のデフォルトのアプローチにする前に少し考えてください。

于 2015-03-13T15:22:42.410 に答える
5

使用HashWithIndifferentAccessは素晴らしいですが、それでもハッシュのように機能し、データベースでYAMLとしてのみシリアル化できます。

私の好みは、Postgres 9.3以降を使用して、Postgresでjson列型を使用することです。これは、テーブルが読み取られると、ActiveRecordがPostgresから直接ハッシュを取得することを意味します。

create_table "gadgets" do |t|
  t.json "info"
end

ActiveRecordでは、データの読み取り/書き込みシリアル化/逆シリアル化serializeの両方を担当する単一のクラスを提供する必要があります。

HashWithIndifferentAccessしたがって、から、または私の好みを継承することによって、ジョブを実行するオブジェクトを作成できますHashie::Mashdump次に、およびloadクラスメソッドとしてシリアル化を実装します。

class HashieMashStoredAsJson < Hashie::Mash
  def self.dump(obj)
    ActiveSupport::JSON.encode(obj.to_h)
  end


  def self.load(raw_hash)
    new(raw_hash || {}) 
  end
end

モデルでは、このクラスをシリアル化用に指定できます。

class Gadget < ActiveRecord::Base
  serialize :info, HashieMashStoredAsJson

  # This allows the field to be set as a Hash or anything compatible with it.
  def info=(new_value)
    self[:info] = HashieMashStoredAsJson.new new_value
  end
end

Postgresでjson列型を使用しない場合、実装はわずかに変更されます

完全なコードとドキュメントはこちら:JSON列型文字列列型を使用します

于 2016-02-01T04:21:45.700 に答える