如何在Rails中将XML转换为哈希?
如何在Ruby中将XML主体转换为哈希?
我有一个XML体,我想解析成哈希
2010-11-10T09:00:00 2010-11-10T09:20:00 2010-11-10T09:20:00 2010-11-10T09:40:00 2010-11-10T09:40:00 2010-11-10T10:00:00 2010-11-10T10:00:00 2010-11-10T10:20:00 2010-11-10T10:40:00 2010-11-10T11:00:00
我想把它转换成这样的哈希:
{ :times_in_my_day => { :time_data = > [ {:start_time=>"2010-11-10T09:00:00", :end_time => "2010-11-10T09:20:00" }, {:start_time=>"2010-11-10T09:20:00", :end_time => "2010-11-10T09:40:00" }, {:start_time=>"2010-11-10T09:40:00", :end_time => "2010-11-10T10:00:00" }, {:start_time=>"2010-11-10T10:00:00", :end_time => "2010-11-10T10:20:00" }, {:start_time=>"2010-11-10T10:40:00", :end_time => "2010-11-10T11:00:00" } ] } }
理想情况下,标记会转换为snake_case符号并成为哈希中的键。
此外,日期时间缺少其时区偏移。 它们位于当地时区(不是UTC)。 所以我想解析它以显示本地偏移量,然后将xml日期时间字符串转换为Rails DateTime对象。 结果数组将是这样的:
{ :times_in_my_day => { :time_data = > [ {:start_time=>Wed Nov 10 09:00:00 -0800 2010, :end_time => Wed Nov 10 9:20:00 -0800 2010 }, {:start_time=>Wed Nov 10 09:20:00 -0800 2010, :end_time => Wed Nov 10 9:40:00 -0800 2010 }, {:start_time=>Wed Nov 10 09:40:00 -0800 2010, :end_time => Wed Nov 10 10:00:00 -0800 2010 }, {:start_time=>Wed Nov 10 10:00:00 -0800 2010, :end_time => Wed Nov 10 10:20:00 -0800 2010 }, {:start_time=>Wed Nov 10 10:40:00 -0800 2010, :end_time => Wed Nov 10 11:00:00 -0800 2010 } ] } }
我能够用这种方式用parse
和in_time_zone
方法转换单个日期时间:
Time.parse(xml_datetime).in_time_zone(current_user.time_zone)
但是我不太确定在将XML转换为哈希时解析时间的最佳方法。
我很感激任何建议。 谢谢!
编辑
将datetime字符串转换为Rails DateTime对象的代码是错误的。 这会将xml日期时间字符串解析为系统的时区偏移量,然后将该时间转换为用户的时区。 正确的代码是:
Time.zone.parse(xml_datetime)
如果用户具有不同于系统的时区,则会将用户的时区偏移量添加到原始日期时间字符串。 有关如何在此处启用用户时区首选项的Railscast: http : //railscasts.com/episodes/106-time-zones-in-rails-2-1 。
我以前在Perl中使用XML :: Simple,因为使用Perl解析XML是一个PITA。
当我切换到Ruby时,我最终使用了Nokogiri,发现它非常容易用于解析HTML和XML。 我认为在CSS或XPath选择器方面很容易,并且不要错过XML-to-hash转换器。
require 'ap' require 'date' require 'time' require 'nokogiri' xml = %{ 2010-11-10T09:00:00 2010-11-10T09:20:00 2010-11-10T09:20:00 2010-11-10T09:40:00 2010-11-10T09:40:00 2010-11-10T10:00:00 2010-11-10T10:00:00 2010-11-10T10:20:00 2010-11-10T10:40:00 2010-11-10T11:00:00 } time_data = [] doc = Nokogiri::XML(xml) doc.search('//TIME_DATA').each do |t| start_time = t.at('StartTime').inner_text end_time = t.at('EndTime').inner_text time_data << { :start_time => DateTime.parse(start_time), :end_time => Time.parse(end_time) } end puts time_data.first[:start_time].class puts time_data.first[:end_time].class ap time_data[0, 2]
输出看起来像:
DateTime Time [ [0] { :start_time => #, :end_time => 2010-11-10 09:20:00 -0700 }, [1] { :start_time => # , :end_time => 2010-11-10 09:40:00 -0700 } ]
故意将时间值解析为DateTime和Time对象,以显示可以使用其中任何一个。
Hash.from_xml(xml)
是解决此问题的简单方法。 它的activesupport方法
ActiveSupport添加了一个Hash.from_xml
,它在一次调用中进行转换。 在另一个问题中描述: https : //stackoverflow.com/a/7488299/937595
例:
require 'open-uri' remote_xml_file = "https://www.example.com/some_file.xml" data = Hash.from_xml(open(remote_xml_file))
最初的问题是在前一段时间被问到的,但我找到了比使用Nokogiri并在XML中搜索特定名称更简单的解决方案。
Nori.parse(your_xml)
将XML解析为哈希,并且键与XML项具有相同的名称。
如果你不介意使用gem,那么破解就可以了。
Crack对XML进行哈希处理,然后您可以循环生成的哈希以规范化日期时间。
编辑使用REXML,你可以尝试以下(应该接近工作,但我没有访问终端,所以它可能需要一些调整):
require 'rexml/document' arr = [] doc = REXML::XPath.first(REXML::Document.new(xml), "//soap:Body/TimesInMyDAY").text REXML::XPath.each(doc, "//TIME_DATA") do |el| start = REXML::XPath.first(el, "//StartTime").text end = REXML::XPath.first(el, "//EndTime").text arr.push({:start_time => Time.parse(start).in_time_zone(current_user.time_zone), :end_time => Time.parse(end).in_time_zone(current_user.time_zone)}) end hash = { :times_in_my_day => { :time_data => arr } }
当然,这假设结构始终是相同的,并且您发布的示例并非为简单起见而设计(通常是示例)。