【问题标题】:Ruby extend DateTime only in classRuby 仅在类中扩展 DateTime
【发布时间】:2018-06-26 13:21:46
【问题描述】:

我想更改DateTime 在Hash#to_json 中的序列化方式以获得更高的精度。

require 'date'
require 'json'
puts ({my_time: DateTime.now}.to_json) # => {"my_time":"2018-01-17T15:34:30+00:00"}

这个工作示例包括毫秒:

require 'date'
require 'json'
class A
  class ::DateTime
    def as_json(*)
      iso8601(3)
    end

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

  def serialize_hash
    {my_time: DateTime.now}.to_json
  end
end

这会覆盖DateTime#to_json 以产生预期的结果,但更改是全局的。我需要在 only 类A 中更改DateTime.to_json。

# Includes milliseconds, as expected
puts A.new.serialize_hash # => {"my_time":"2018-01-17T15:33:44.348+00:00"}

# Outside class A
# Should not include milliseconds
puts ({my_other_time: DateTime.now}.to_json) # => {"my_other_time":"2018-01-17T15:33:44.348+00:00"}

这种全局变化使我的一个 gem 无法加载 TypeError: superclass mismatch for class DateTime。

这是一个纯 Ruby 实现。没有导轨。

编辑:根据@SergioTulentsev 的建议,我现在正在改进。

require 'date'
require 'json'
module DateTimeExtensions
  refine DateTime do
    def as_json(*)
      iso8601(3)
    end

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

看来Hash#to_json 看不到我的细化。

using DateTimeExtensions
# Has milliseconds, as expected
puts DateTime.now.to_json # => "2018-01-17T16:23:46.043+00:00"
# Should also have milliseconds, but does not
puts ({my_time: DateTime.now}.to_json) # => {"my_time":"2018-01-17T16:23:46+00:00"}

【问题讨论】:

  • 这正是细化所做的。阅读此内容。
  • @SergioTulentsev 或者这正是对iso8601 的显式调用:{my_time: DateTime.now.iso8601(3)}.to_json :)
  • 阅读这篇文章,了解 Refinements 的工作原理jakeyesbeck.com/2015/12/13/ruby-refinements

标签: json ruby datetime serialization


【解决方案1】:

这会将散列(和嵌套散列)中的所有 DateTime 值手动序列化为指定格式。在此之后my_hash.to_json 将显示正确的精度。

require 'date'
require 'json'

module HashExtensions
  refine Hash do
    def deep_traverse(&block)
      stack = self.map{ |k,v| [ [k], v ] }
      while not stack.empty?
        key, value = stack.pop
        yield(key, value)
        if value.is_a? Hash
          value.each{ |k,v| stack.push [ key.dup << k, v ] }
        end
      end
    end

    def deep_update(&block)
      deep_traverse do |keys, v|
        if keys.length == 1
          self[keys.first] = block.call(v)
        else
          self.dig(*keys.first(keys.length - 1))&.[]=(keys.last, block.call(v))
        end
      end
    end
  end
end

测试嵌套哈希:

h = {
  d1: DateTime.now,
  level1: {
    d2: DateTime.now,
    level2: {
      d3: DateTime.now,
    }
  }
}
using HashExtensions
h.deep_update do |v|
  if v.is_a? DateTime
     v.iso8601(3)
  else
    v
  end
end
puts h

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-27
    • 2019-12-16
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多