如何使用 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"
我开始使用 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"