我正在使用 Ruby(在我的机器上为 ruby 2.1.2p95 (2014-05-08) [x86_64-linux-gnu],在生产环境中为 ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux])和 Nori 将 XML 文档(最初使用 Nokogiri 处理以进行一些验证)转换为 Ruby 哈希,但后来我发现 Nori正在删除最深的 XML 元素的属性。
为此,我使用了类似于以下的代码:
xml = Nokogiri::XML(File.open('file.xml')) { |config| config.strict.noblanks }
hash = Nori.new.parse xml.to_s
除一种情况外,代码通常按预期工作。每当 Nori 解析 XML 文本时,它都会从叶元素(即没有子元素的元素)中删除元素属性。
例如下面的文件:
<?xml version="1.0"?>
<root>
<objects>
<object>
<fields>
<id>1</id>
<name>The name</name>
<description>A description</description>
</fields>
</object>
</objects>
</root>
...转换为预期的哈希(为简洁起见省略了一些输出):
irb(main):066:0> xml = Nokogiri::XML(txt) { |config| config.strict.noblanks }
irb(main):071:0> ap Nori.new.parse(xml.to_s), :indent => -2
{
"root" => {
"objects" => {
"object" => {
"fields" => {
"id" => "1",
"name" => "The name"
"description" => "A description"
}
}
}
}
}
当元素属性用于没有子元素的元素时,问题就会出现。例如,以下文档未按预期转换:
<?xml version="1.0"?>
<root>
<objects>
<object id="1">
<fields>
<field name="Name">The name</field>
<field name="Description">A description</field>
</fields>
</object>
</objects>
</root>
同Nori.new.parse(xml.to_s) , 如 awesome_print 所示, 显示最深的属性 <field>元素不存在:
irb(main):131:0> ap Nori.new.parse(xml.to_s), :indent => -2
{
"root" => {
"objects" => {
"object" => {
"fields" => {
"field" => [
[0] "The name",
[1] "A description"
]
},
"@id" => "1"
}
}
}
}
哈希仅将它们的值作为列表,这不是我想要的。我期待 <field>元素保留它们的属性,就像它们的父元素一样(例如,参见 @id="1" 的 <object> ),而不是为了它们的属性被切断。
即使将文档修改成如下所示,它仍然无法按预期工作:
<?xml version="1.0"?>
<root>
<objects>
<object id="1">
<fields>
<Name type="string">The name</Name>
<Description type="string">A description</Description>
</fields>
</object>
</objects>
</root>
它产生以下哈希:
{
"root" => {
"objects" => {
"object" => {
"fields" => {
"Name" => "The name",
"Description" => "A description"
},
"@id" => "1"
}
}
}
}
缺少 type="whatever"每个字段条目的属性。
搜索最终将我引向 Issue #59最后一篇文章(2015 年 8 月)说他无法“找到 Nori 代码中的错误。”
所以,我的问题是:你们中有人知道解决 Nori 问题的方法(例如,可能是一种设置),可以让我使用我的原始模式(即带有没有 child 的元素中的属性)?如果是这样,您能否分享一个能够正确处理此问题的代码片段?
我不得不重新设计我的 XML 架构并更改代码大约 3 次才能使其正常工作,所以如果有一种方法可以让 Nori 正常工作,而我根本不知道它,我想知道它是什么。
我想避免尽可能多地安装更多的库,只是为了让它与我最初想使用的模式结构一起正常工作,但如果它被证明,我愿意接受这种可能性上类。 (我不得不再次重构代码...)框架对此肯定是矫枉过正,所以请:不要建议 Ruby on Rails或类似的全栈解决方案。
请注意,我当前的解决方案基于(不情愿地)重新设计的模式,它正在运行,但生成和处理比原始模式更复杂,我想回到更简单/更浅的模式。
最佳答案
Nori 实际上并没有删除属性,它们只是没有被打印出来。
如果您运行 ruby 脚本:
require 'nori'
data = Nori.new(empty_tag_value: true).parse(<<XML)
<?xml version="1.0"?>
<root>
<objects>
<object>
<fields>
<field name="Name">The name</field>
<field name="Description">A description</field>
</fields>
</object>
</objects>
</root>
XML
field_list = data['root']['objects']['object']['fields']['field']
puts "text: '#{field_list[0]}' data: #{field_list[0].attributes}"
puts "text: '#{field_list[1]}' data: #{field_list[1].attributes}"
你应该得到输出
["The name", "A description"]
text: 'The name' data: {"name"=>"Name"}
text: 'A description' data: {"name"=>"Description"}
这清楚地表明该属性在那里,但是 inspect 方法没有显示(p(x) 函数与 puts x 相同.检查)。
您会注意到 puts field_list.inspect 输出 ["The name", "A description"]。但是 field_list[0].attributes 打印属性键和数据。
如果你想让 pp 显示它,你可以重载 Nori::StringWithAttributes 中的 inspect 方法。
class Nori
class StringWithAttributes < String
def inspect
[attributes, String.new(self)].inspect
end
end
end
或者,如果您想更改输出,您可以重载 self.new 方法,让它返回不同的数据结构。
class Nori
class MyText < Array
def attributes=(data)
self[1] = data
end
attr_accessor :text
def initialize(text)
self[0] = text
self[1] = {}
end
end
class StringWithAttributes < String
def self.new(x)
MyText.new(x)
end
end
end
并以元组的形式访问数据
puts "text: '#{data['root']['objects']['object']['fields']['field'][0].first}' data: #{ data['root']['objects']['object']['fields']['field'][0].last}"
这样您就可以将数据作为 JSON 或 YAML,因为文本项看起来像具有 2 个元素的数组。
pp 也可以。
{"root"=>
{"objects"=>
{"object"=>
{"fields"=>
{"field"=>
[["The name", {"name"=>"Name"}],
["A description", {"name"=>"Description"}]]},
"bob"=>[{"@id"=>"id1"}, {"@id"=>"id2"}],
"bill"=>
[{"p"=>["one", {}], "@id"=>"bid1"}, {"p"=>["two", {}], "@id"=>"bid2"}],
"@id"=>"1"}}}}
这应该做你想做的。
require 'awesome_print'
require 'nori'
# Copyright (c) 2016 G. Allen Morris III
#
# Awesome Print is freely distributable under the terms of MIT license.
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
module AwesomePrint
module Nori
def self.included(base)
base.send :alias_method, :cast_without_nori, :cast
base.send :alias_method, :cast, :cast_with_nori
end
# Add Nori XML Node and NodeSet names to the dispatcher pipeline.
#-------------------------------------------------------------------
def cast_with_nori(object, type)
cast = cast_without_nori(object, type)
if defined?(::Nori::StringWithAttributes) && object.is_a?(::Nori::StringWithAttributes)
cast = :nori_xml_node
end
cast
end
#-------------------------------------------------------------------
def awesome_nori_xml_node(object)
return %Q|["#{object}", #{object.attributes}]|
end
end
end
AwesomePrint::Formatter.send(:include, AwesomePrint::Nori)
data = Nori.new(empty_tag_value: true).parse(<<XML)
<?xml version="1.0"?>
<root>
<objects>
<object>
<fields>
<field name="Name">The name</field>
<field name="Description">A description</field>
</fields>
</object>
</objects>
</root>
XML
ap data
因为输出是:
{
"root" => {
"objects" => {
"object" => {
"fields" => {
"field" => [
[0] ["The name", {"name"=>"Name"}],
[1] ["A description", {"name"=>"Description"}]
]
}
}
}
}
}
关于ruby - XML 到哈希转换 : Nori drops the attributes of the deepest XML elements,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35735053/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby类,但是我如何得到ActiveRecord关联这个类模型
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits