2012-11-27 127 views
2

我想将Ruby DateTime对象序列化为json。不幸的是,我的方法不是对称的:我可以使用一些任意的strftime/parse字符串组合,但我相信必须有更好的方法。DateTime序列化和反序列化

回答

5

不幸的是,接受的答案不是一个好的解决方案。像往常一样,编组/解组是一种工具,你应该只用作最后的手段,但在这种情况下,它可能会破坏你的应用程序。

OP明确提到将日期序列化为JSON。每RFC 7159

JSON文本应以UTF-8,UTF-16或UTF-32编码。默认编码是UTF-8,以UTF-8编码的JSON文本是可互操作的,因为它们将通过最大数量的实现成功读取;有很多实现无法成功读取其他编码中的文本(如UTF-16和UTF-32)。

现在让我们来看看我们从元帅得到:

marsh = Marshal.dump(DateTime.now) 
# => "\x04\bU:\rDateTime[\vi\x00i\x03\xE0\x7F%i\x02s\xC9i\x04\xF8z\xF1\"i\xFE\xB0\xB9f\f2299161" 
puts marsh.encoding 
# -> #<Encoding:ASCII-8BIT> 

marsh.encode(Encoding::UTF_8) 
# -> Encoding::UndefinedConversionError: "\xE0" from ASCII-8BIT to UTF-8 

除了返回的值是不是人类可读,Marshal.dump给了我们不能转换为UTF值-8。这意味着将其放入(有效)JSON的唯一方法是以某种方式对其进行编码,例如,基64。

没有必要这样做。已经有一种可以互操作的方式来表示日期和时间:ISO 8601。我不会回顾为什么它是JSON的最佳选择(一般而言),但这里的答案覆盖了它:The "right" JSON date format

由于Ruby 1.9.3的DateTime类已分别有iso8601classinstance方法分析和格式化ISO 8601日期。后者采用参数来指定小数秒的精度(例如,3为毫秒):

require "date" 

date = DateTime.now 
str = date.iso8601(9) 
puts str 
# -> 2016-06-28T09:35:58.311527000-05:00 

DateTime.iso8601(str) == date 
# => true 

注意,如果你指定一个较小的精度,这可能不行,因为如58.311不等于58.311527。精确度为9(纳秒)对我来说似乎是安全的,因为DateTime文档说:

小数的精度假定在最大纳秒。

但是,如果您正在与可能使用更高精度的系统进行互操作,则应考虑这一点。

最后,如果你想使Ruby的JSON库自动使用iso8601系列化,覆盖as_jsonto_json方法:

unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED 
    require 'json' 
end 
require 'date' 

class DateTime 
    def as_json(*) 
    iso8601(9) 
    end 

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

puts DateTime.now.to_json 
# -> "2016-06-28T09:35:58.311527000-05:00" 
+0

感谢您详细的分析,我是不知道的问题,使用UTF-8 – jupp0r

2

这是因为date有次秒值,并且#to_s方法会以秒为单位返回ISO时间格式,比较不成功。

1.9.3p327 :021 > date = DateTime.now 
=> #<DateTime: 2012-11-28T07:32:40+09:00 ((2456259j,81160s,283019000n),+32400s,2299161j)> 
1.9.3p327 :022 > DateTime.parse(date.to_s) 
=> #<DateTime: 2012-11-28T07:32:40+09:00 ((2456259j,81160s,0n),+32400s,2299161j)> 

所以他们实际上是不同的。

如果你不关心亚秒,只是忘记比较是否成功。

或者,您可以使用DateTime#marshal_loadDateTime#marshal_dump作为1.9.3。 (我不知道这一点到现在..)

它的工作:

date1 = DateTime.now 
dump = date1.marshal_dump 
date2 = DateTime.new.marshal_load(dump) 
date1 == date2 # => true 
3

无论是to_s方法和to_json方法(提供require 'json')忽略它们由DateTime对象存储在纳秒date。好老Marshal发送:

require 'date' 
date = DateTime.now 
m_date = Marshal.dump(date) 
p Marshal.load(m_date) == date # => true