如何使用 nokogiri-happymapper 和 roxml 从不缩进的对象生成 XML

How to generate XML from an object without indentation using nokogiri-happymapper and roxml

我开始使用 nokogiri-happymapper and roxml 将 Ruby 对象转换为 XML。如果没有缩进 ("\n") 和说明,我无法生成 XML。

是否有为 to_xml 方法设置 :indent=>0, :skip_instruct 的选项,就像我们在 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 指令时删除缩进?

我尝试了以下方法: 方法一:

 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" 

方法二:

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 ??

如果查了文档,咨询了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 库将如果它认为会导致返回无效的 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"