如何使用nokogiri-happymapper和roxml

时间:2016-03-02 12:34:40

标签: ruby xml nokogiri indentation

我开始使用nokogiri-happymapperroxml将Ruby对象转换为XML。没有缩进(" \ n")而没有指令,我无法生成XML。

是否可以选择为:indent=>0, :skip_instruct方法设置to_xml,就像我们在nokogiri-happymapper和roxml中为Active Support设置的那样?

此外,当我尝试使用roxml将XML转换为对象时,我得到一个包含@roxml_references的字符串。如何正确地将XML转换为Ruby对象?

ROXML代码是:

require 'roxml'
class Book
  include ROXML

  xml_accessor :isbn
  xml_accessor :title
  xml_accessor :description
  xml_accessor :author
end

book = Book.new
book.author = "ABC"
book.title = "Ruby"
doc = Nokogiri::XML::Document.new
doc.root = book.to_xml
puts doc.to_s

输出:

"<?xml version=\"1.0\"?>\n<book>\n  <title>Ruby</title>\n  <author>ABC</author>\n</book>\n"

obj = Book.from_xml(doc.to_s)
puts obj

输出:

#<Mod::Book:0x00000003141718 @author="ABC", @title="Ruby", @roxml_references=[#<ROXML::XMLTextRef:0x00000003141650 @opts=#<ROXML::Definition
:0x000000031b93f8 @default=nil, @to_xml=nil, @name_explicit=false, @cdata=nil, @required=nil, @frozen=nil, @wrapper=nil, @namespace=nil, @ac
cessor="isbn", @array=false, @blocks=[], @sought_type=:text, @attr_name="isbn", @name="isbn">, @instance=#<Mod::Book:0x00000003141718 ...>,
  @default_namespace=nil>, #<ROXML::XMLTextRef:0x00000003141628 @opts=#<ROXML::Definition:0x000000031b8930 @default=nil, @to_xml=nil, @name_ex
  plicit=false, @cdata=nil, @required=nil, @frozen=nil, @wrapper=nil, @namespace=nil, @accessor="title", @array=false, @blocks=[], @sought_typ
e=:text, @attr_name="title", @name="title">, @instance=#<Mod::Book:0x00000003141718 ...>, @default_namespace=nil>, #<ROXML::XMLTextRef:0x000
  00003141600 @opts=#<ROXML::Definition:0x000000031a3fa8 @default=nil, @to_xml=nil, @name_explicit=false, @cdata=nil, @required=nil, @frozen=n
  il, @wrapper=nil, 

nokogiri-happymapper代码是:

require 'happymapper'

class Book
  include HappyMapper

  attr_accessor :title,:author
  tag 'book'
  element :title, String, :tag => 'title'
  element :author, String, :tag => 'author'
end

book = Mod::Book.new
book.author = "ABC"
book.title = "Ruby"

xml_obj = book.to_xml
p xml_obj

输出:

"<?xml version=\"1.0\"?>\n<book>\n  <title>Ruby</title>\n  <author>ABC</author>\n</book>\n"

obj = Mod::Book.parse(xml_obj)
p obj

输出:

#<Mod::Book:0x00000000661cf0 @author="ABC", @title="Ruby">

如何从对象生成XML时删除缩进,以及两种方法的XML指令?

我尝试过以下方法: 方法1:

 xml =  Nokogiri::XML(xml_obj).to_xml(:save_with =>  Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
 p xml

输出

"<book>\n  <title>Ruby</title>\n  <author>ABC</author>\n</book>\n" 

方法2:

xml = Nokogiri::XML::Document.parse(xml_obj, nil,nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_s
p xml 

输出

"<book>\n  <title>Ruby</title>\n  <author>ABC</author>\n</book>"

我使用以下方法将对象转换为roxml中的xml:

xml_obj = lib.to_xml.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)
p xml_obj

输出:

"<Library><author><name>Shruti</name></author><book><title>RoR</title></book></Library>"

现在,当我尝试将xml转换回对象时,它为我提供了一个额外的实例变量@roxml_references,如下所示:

obj = Library.from_xml(xml_obj)
p obj

输出:

#<Library:0x00000002a1ebc0 @author=#<Author:0x00000002a1c780 @name="Shruti", @roxml_references=[#<ROXML::XMLTextRef:0x00000002a1e1e8 @opts=#
<ROXML::Definition:0x00000002a46418 @default=nil, @to_xml=nil, @name_explicit=false, @cdata=nil, @required=nil, @frozen=nil, @wrapper=nil, @
namespace=nil, @accessor="name", @array=false, @blocks=[], @sought_type=:text, @attr_name="name", @name="name">, @instance=#<Author:0x000000
02a1c780 ...>, @default_namespace=nil>]>, @book=[#<Book:0x00000002a08e60 @title="RoR", @roxml_references=[#<ROXML::XMLTextRef:0x00000002a092
e8 @opts=#<ROXML::Definition:0x00000002a3e8d0 @default=nil, @to_xml=nil, @name_explicit=false, @cdata=nil, @required=nil, @frozen=nil, @wrap
per=nil, @namespace=nil, @accessor="title", @array=false, @blocks=[], @sought_type=:text, @attr_name="title", @name="title">, @instance=#<Bo
ok:0x00000002a08e60 ...>, @default_namespace=nil>, #<ROXML::XMLTextRef:0x00000002a09400 @opts=#<ROXML::Definition:0x00000002a3d6b0 @default=
nil, @to_xml=nil, @name_explicit=false, @cdata=nil, @required=nil, @frozen=nil, @wrapper=nil, @namespace=nil, @accessor="description", @arra
y=false, @blocks=[], @sought_type=:text, @attr_name="description", @name="description">, @instance=#<Book:0x00000002a08e60 ...>, @default_na
mespace=nil>], @description=nil>], @roxml_references=[#<ROXML::XMLObjectRef:0x00000002a1eb20 @opts=#<ROXML::Definition:0x00000002a3c080 @def
ault=nil, @to_xml=nil, @name_explicit=false, @cdata=nil, @required=nil, @frozen=nil, @wrapper=nil, @namespace=nil, @accessor="author", @arra
y=false, @blocks=[], @sought_type=Author, @attr_name="author", @name="author">, @instance=#<Library:0x00000002a1ebc0 ...>, @default_namespac
e=nil>, #<ROXML::XMLObjectRef:0x00000002a1eaf8 @opts=#<ROXML::Definition:0x00000002a373c8 @default=nil, @to_xml=nil, @name_explicit=false, @
cdata=nil, @required=nil, @frozen=nil, @wrapper=nil, @namespace=nil, @accessor="book", @array=true, @blocks=[], @sought_type=Book, @attr_nam
e="book", @name="book">, @instance=#<Library:0x00000002a1ebc0 ...>, @default_namespace=nil>]>

有没有办法可以从创建的对象中删除@roxml_references

1 个答案:

答案 0 :(得分:1)

如果在搜索文档并咨询gem的作者后仍然找不到解决方案,那么让Nokogiri解析输出,删除节点并重新输出它而不缩进。

考虑一下:

require 'nokogiri'

xml = <<EOT
<root>
</root>
EOT

Nokogiri::XML(xml)
# => #<Nokogiri::XML::Document:0x3ffd49419494 name="document" children=[#<Nokogiri::XML::Element:0x3ffd49419084 name="root" children=[#<Nokogiri::XML::Text:0x3ffd49418df0 "\n">]>]>

注意上面包含“\ n”的Nokogiri :: XML :: Text节点。那是XML中<root>之后的行尾:

doc.to_xml # => "<?xml version=\"1.0\"?>\n<root>\n</root>\n"

以下是我们如何找到文本节点:

doc.search('//text()') # => [#<Nokogiri::XML::Text:0x3fff88c18d20 "\n">]

'//text()'是一个XPath选择器,意思是“在整个文档中搜索文本节点。

我们可以遍历DOM并删除那些空节点:

doc.search('//text()').each do |text_node|
  text_node.unlink 
end

doc.to_xml # => "<?xml version=\"1.0\"?>\n<root/>\n"

我们必须要小心,因为Nokogiri :: XML :: Text节点不仅可以包含尾随行尾,因此不加选择的节点删除也会删除所需的文本。我们也可以删除节点的内容,使其成为空的:

xml = <<EOT
<root>
  <foo>bar</foo>
</root>
EOT

doc = Nokogiri::XML(xml)
doc.search('//text()') # => [#<Nokogiri::XML::Text:0x3ff77201927c "\n  ">, #<Nokogiri::XML::Text:0x3ff772018e80 "bar">, #<Nokogiri::XML::Text:0x3ff772018c14 "\n">]
doc.search('//text()').each do |text_node|
  text_node.content = '' 
end

doc.to_xml # => "<?xml version=\"1.0\"?>\n<root><foo></foo></root>\n"

但请注意删除了所需的文字“bar”。解决方案是更具选择性:

doc.search('//text()').each do |text_node|
  text_node.content = '' if text_node.content.strip.empty?
end

doc.to_xml # => "<?xml version=\"1.0\"?>\n<root><foo>bar</foo></root>\n"

注意:Nokogiri包含一个NOBLANKS解析选项,旨在帮助删除缩进节点,但根据“Unexpected behavior with XML_PARSE_NOBLANKS”,如果它认为'底层libXML2库将不会忽略空白d导致返回无效的DOM。

如果您不想使用XMLdecl,可以告诉Nokogiri将文档解析为DocumentFragment:

xml = <<EOT
<root>
</root>
EOT

doc = Nokogiri::XML(xml)
doc.to_xml # => "<?xml version=\"1.0\"?>\n<root>\n</root>\n"

doc = Nokogiri::XML::DocumentFragment.parse(xml)
doc.to_xml # => "<root>\n</root>\n"