【问题标题】:How can I control which fields to serialize with YAML如何控制使用 YAML 序列化的字段
【发布时间】:2012-05-24 15:29:34
【问题描述】:

例如,

class Point
  attr_accessor :x, :y, :pointer_to_something_huge
end

我只想序列化 x 和 y 并将其他所有内容都保留为 nil。

【问题讨论】:

    标签: ruby yaml


    【解决方案1】:

    在 Ruby 1.9 中,to_yaml_properties is deprecated;如果您使用的是 Ruby 1.9,一个更面向未来的方法是使用encode_with

    class Point
      def encode_with coder
        coder['x'] = @x
        coder['y'] = @y
      end
    end
    

    在这种情况下,这就是您所需要的,因为默认情况是在从 Yaml 加载时将新对象的相应实例变量设置为适当的值,但在更复杂的情况下,您可以使用init_with

    def init_with coder
      @x = coder['x']
      @y = coder['y']
    end
    

    【讨论】:

    • 仅供参考:如果您为 ActiveRecord::Base 子类覆盖 #init_with,请记住返回 self,否则任何时候实例化模型都会返回 nil。
    • encode_with 还有什么作用? to_json还有朋友吗?
    【解决方案2】:

    经过大量搜索后,我偶然发现了这个:

    class Point
      def to_yaml_properties
        ["@x", "@y"]
      end
    end
    

    此方法用于选择 YAML 序列化的属性。有一种更强大的方法涉及自定义发射器(在 Psych 中),但我不知道它是什么。

    此解决方案仅适用于 Ruby 1.8;在 Ruby 1.9 中,to_yaml 已切换到使用 Psych,对此,Matt 使用 encode_with 的答案是合适的解决方案。

    【讨论】:

      【解决方案3】:

      如果您想要除少数几个以外的所有字段,您可以这样做

      def encode_with(coder)
        vars = instance_variables.map{|x| x.to_s}
        vars = vars - ['@unwanted_field1', '@unwanted_field2']
      
        vars.each do |var|
          var_val = eval(var)
          coder[var.gsub('@', '')] = var_val
        end
      end
      

      这使您不必手动管理列表。在 Ruby 1.9 上测试

      【讨论】:

        【解决方案4】:

        如果你有很多实例变量,你可以使用像这样的简短版本

        def encode_with( coder )
          %w[ x y a b c d e f g ].each { |v| coder[ v ] = instance_variable_get "@#{v}" }
        end
        

        【讨论】:

          【解决方案5】:

          您应该使用#encode_with,因为不推荐使用#to_yaml_properties:

          def encode_with(coder)
            # remove @unwanted and @other_unwanted variable from the dump
            (instance_variables - [:@unwanted, :@other_unwanted]).each do |var|
              var = var.to_s                       # convert symbol to string
              coder[var.gsub('@', '')] = eval(var) # set key and value in coder hash
            end
          end
          

          如果 eval 太危险并且您只需要过滤掉一个实例 var,您可能更喜欢这个。所有其他变量都需要有一个访问器:

          attr_accessor :keep_this, :unwanted
          
          def encode_with(coder)
            # reject @unwanted var, all others need to have an accessor
            instance_variables.reject{|x|x==:@unwanted}.map(&:to_s).each do |var|
              coder[var[1..-1]] = send(var[1..-1])
            end
          end
          

          【讨论】:

          • 在你的答案中添加一些 cmets。
          【解决方案6】:

          我建议在你的类中添加一个自定义的 to_yaml 方法来构造你想要的特定 yaml 格式。

          我知道to_json 接受参数来告诉它要序列化哪些属性,但我找不到to_yaml 相同的属性。

          这是to_yaml的实际来源:

          # File activerecord/lib/active_record/base.rb, line 653
          
            def to_yaml(opts = {}) #:nodoc:
              if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
                super
              else
                coder = {}
                encode_with(coder)
                YAML.quick_emit(self, opts) do |out|
                  out.map(taguri, to_yaml_style) do |map|
                    coder.each { |k, v| map.add(k, v) }
                  end
                end
              end
            end
          

          所以看起来可能有机会设置 opts 以便它在 yaml 中包含特定的键/值对。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-01-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-18
            • 2013-04-25
            • 2023-01-12
            相关资源
            最近更新 更多