从以下文档XML::Simple
:
不鼓励在新代码中使用此模块。其他模块也可以提供更直接和一致的接口。特别是,强烈推荐使用 XML::LibXML。
这个模块的主要问题是大量的选项以及这些选项交互的任意方式——通常会产生意想不到的结果。
有人可以为我澄清造成这种情况的主要原因是什么吗?
从以下文档XML::Simple
:
不鼓励在新代码中使用此模块。其他模块也可以提供更直接和一致的接口。特别是,强烈推荐使用 XML::LibXML。
这个模块的主要问题是大量的选项以及这些选项交互的任意方式——通常会产生意想不到的结果。
有人可以为我澄清造成这种情况的主要原因是什么吗?
真正的问题是,XML::Simple
主要尝试做的是采用 XML,并将其表示为 perl 数据结构。
毫无疑问,您可以从perldata
两个可用的关键数据结构中知道,hash
和array
.
XML 也不是真的。它具有以下元素:
而且这些东西并不直接映射到可用的 perl 数据结构——在简单的层面上,散列的嵌套散列可能适合——但它不能处理具有重复名称的元素。您也不能轻易区分属性和子节点。
因此XML::Simple
尝试根据 XML 内容进行猜测,并从各种选项设置中获取“提示”,然后当您尝试输出内容时,它(尝试)反向应用相同的过程。
结果,对于除了最简单的XML 之外的任何东西,它充其量会变得笨拙,或者最坏的情况会丢失数据。
考虑:
<xml>
<parent>
<child att="some_att">content</child>
</parent>
<another_node>
<another_child some_att="a value" />
<another_child different_att="different_value">more content</another_child>
</another_node>
</xml>
这 - 通过解析XML::Simple
给你:
$VAR1 = {
'parent' => {
'child' => {
'att' => 'some_att',
'content' => 'content'
}
},
'another_node' => {
'another_child' => [
{
'some_att' => 'a value'
},
{
'different_att' => 'different_value',
'content' => 'more content'
}
]
}
};
注意 - 现在你有parent
- 只是匿名散列,但another_node
你有一个匿名散列数组。
因此,为了访问以下内容child
:
my $child = $xml -> {parent} -> {child} -> {content};
注意你是如何得到一个“子”节点的,它下面有一个“内容”节点,这不是因为它是……内容。
但是要访问第一个another_child
元素下的内容:
my $another_child = $xml -> {another_node} -> {another_child} -> [0] -> {content};
请注意如何 - 由于有多个<another_node>
元素,XML 已被解析为一个数组,其中没有一个数组。(如果您确实content
在其下方调用了一个元素,那么您最终会得到其他东西)。你可以通过使用来改变它,ForceArray
但你最终会得到一个散列数组的散列数组的散列 - 尽管它至少在处理子元素方面是一致的。编辑:注意,以下讨论 - 这是一个糟糕的默认设置,而不是 XML::Simple 的缺陷。
你应该设置:
ForceArray => 1, KeyAttr => [], ForceContent => 1
如果您将其应用于上述 XML,则会得到:
$VAR1 = {
'another_node' => [
{
'another_child' => [
{
'some_att' => 'a value'
},
{
'different_att' => 'different_value',
'content' => 'more content'
}
]
}
],
'parent' => [
{
'child' => [
{
'att' => 'some_att',
'content' => 'content'
}
]
}
]
};
这将为您提供一致性,因为您将不再让单节点元素以不同于多节点的方式处理。
但你仍然:
例如:
print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};
您仍然将content
和child
散列元素视为属性,并且由于散列是无序的,因此您根本无法重建输入。所以基本上,你必须解析它,然后运行它Dumper
来找出你需要看的地方。
但是通过xpath
查询,您可以通过以下方式到达该节点:
findnodes("/xml/parent/child");
你没有得到XML::Simple
你所做的事情XML::Twig
(我想,XML::LibXML
但我不太清楚):
xpath
支持。xpath
是一种表示节点路径的 XML 方式。因此,您可以使用get_xpath('//child')
. 您甚至可以在 -like 中使用属性,xpath
这get_xpath('//another_child[@different_att]')
将准确选择您想要的。(您也可以迭代匹配项)。cut
并paste
移动元素parsefile_inplace
允许您XML
使用就地编辑进行修改。pretty_print
选项,以格式化XML
.twig_handlers
和purge
- 它允许您处理非常大的 XML,而无需将其全部加载到内存中。simplify
如果你真的必须让它向后兼容XML::Simple
.它也广泛可用 - 易于从 下载CPAN
,并作为可安装包分发到许多操作系统上。(遗憾的是它不是默认安装。然而)
请参阅:XML::Twig 快速参考
为了比较:
my $xml = XMLin( \*DATA, ForceArray => 1, KeyAttr => [], ForceContent => 1 );
print Dumper $xml;
print $xml ->{parent}->[0]->{child}->[0]->{content};
比。
my $twig = XML::Twig->parse( \*DATA );
print $twig ->get_xpath( '/xml/parent/child', 0 )->text;
print $twig ->root->first_child('parent')->first_child_text('child');
XML::Simple 的主要问题是生成的结构极难正确导航。$ele->{ele_name}
可以返回以下任何内容(即使对于遵循相同规范的元素):
[ { att => 'val', ..., content => [ 'content', 'content' ] }, ... ]
[ { att => 'val', ..., content => 'content' }, ... ]
[ { att => 'val', ..., }, ... ]
[ 'content', ... ]
{ 'id' => { att => 'val', ..., content => [ 'content', 'content' ] }, ... }
{ 'id' => { att => 'val', ..., content => 'content' }, ... }
{ 'id' => { att => 'val', ... }, ... }
{ 'id' => { content => [ 'content', 'content' ] }, ... }
{ 'id' => { content => 'content' }, ... }
{ att => 'val', ..., content => [ 'content', 'content' ] }
{ att => 'val', ..., content => 'content' }
{ att => 'val', ..., }
'content'
这意味着您必须执行各种检查才能查看实际得到的结果。但是这种纯粹的复杂性鼓励开发人员做出非常糟糕的假设。这导致各种问题滑入生产环境,当遇到极端情况时,导致实时代码失败。
您可以使用以下选项来创建更规则的树:
ForceArray => 1, KeyAttr => [], ForceContent => 1
但即使有这些选项,仍然需要进行许多检查才能从树中提取信息。例如,/root/eles/ele
从文档中获取节点是一个常见的操作,执行起来应该很简单,但是在使用 XML::Simple 时需要执行以下操作:
# Requires: ForceArray => 1, KeyAttr => [], ForceContent => 1, KeepRoot => 0
# Assumes the format doesn't allow for more than one /root/eles.
# The format wouldn't be supported if it allowed /root to have an attr named eles.
# The format wouldn't be supported if it allowed /root/eles to have an attr named ele.
my @eles;
if ($doc->{eles} && $doc->{eles}[0]{ele}) {
@eles = @{ $doc->{eles}[0]{ele} };
}
在另一个解析器中,将使用以下内容:
my @eles = $doc->findnodes('/root/eles/ele');
它对于生成 XML 完全没用。即使有ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1
,也有太多无法控制的细节。
它不保留具有不同名称的孩子的相对顺序。
它对命名空间和命名空间前缀的支持有限(使用 XML::SAX 后端)或不支持(使用 XML::Parser 后端)。
一些后端(例如 XML::Parser)无法处理不基于 ASCII 的编码(例如 UTF-16le)。
一个元素不能有同名的子元素和属性。
它不能创建带有注释的 XML 文档。
忽略前面提到的主要问题,XML::Simple 在这些限制下仍然可以使用。但是,为什么还要麻烦检查 XML::Simple 是否可以处理您的文档格式并冒着以后不得不切换到另一个解析器的风险呢?您可以从一开始就对所有文档使用更好的解析器。
其他一些解析器不仅不受这些限制的约束,而且还提供了许多其他有用的特性。以下是他们可能具有 XML::Simple 没有的一些特性:
速度。XML::Simple 非常慢,尤其是当您使用 XML::Parser 以外的后端时。我说的是比其他解析器慢几个数量级。
XPath 选择器或类似的。
支持超大文件。
支持漂亮的打印。
XML::Simple 最简单的唯一格式是没有可选元素的格式。我曾使用过无数种 XML 格式,但从未遇到过这样的格式。
仅这种脆弱性和复杂性就足以证明远离 XML::Simple 的理由,但还有其他原因。
我使用 XML::LibXML。它是一个速度极快、功能齐全的解析器。如果我需要处理不适合内存的文档,我会使用 XML::LibXML::Reader(及其copyCurrentNode(1)
)或 XML::Twig(使用twig_roots
)。
我会反对并说这XML::Simple
就是……简单。而且,对我来说,使用它总是很容易和愉快。使用您收到的输入对其进行测试。只要输入没有改变,你就很好。抱怨使用的人也抱怨XML::Simple
使用JSON::Syck
序列化 Moose。文档是错误的,因为它们考虑了正确性而不是效率。如果您只关心以下内容,那么您很好:
如果您正在制作一个不是由应用程序而是由规范定义的抽象解析器,我会使用其他东西。我曾经在一家公司工作,我们不得不接受 300 种不同的 XML 模式,其中没有一种具有规范。XML::Simple
轻松地完成了这项工作。其他选项将要求我们实际雇用某人来完成工作。每个人都认为 XML 是一种以严格的包罗万象的规范格式发送的东西,因此如果您编写一个解析器就可以了。如果是这种情况,请不要使用XML::Simple
. 在 JSON 之前,XML 只是从一种语言到另一种语言的“转储并步行”格式。人们实际上使用了类似的东西XML::Dumper
。没有人真正知道输出了什么。处理这种情况XML::Simple
太棒了!理智的人仍然在没有规范的情况下转储到 JSON 来完成同样的事情。这就是世界的运作方式。
想要读取数据,而不用担心格式?想要遍历 Perl 结构而不是 XML 可能性?去XML::Simple
。
同样,对于大多数应用程序来说,转储并步行JSON::Syck
就足够了。虽然如果您要发送给很多人,我强烈建议您不要成为冲洗喷嘴并制作您导出的规格。但是,你知道吗.. 有时你会接到一个你不想与之交谈的人的电话,他想要他通常不会导出的数据。而且,你要通过's voodoo 管它,让他们担心。如果他们想要 XML?多收他们 500 美元,然后开火。JSON::Syck
XML::Dumper
它可能不够完美,但XML::Simple
非常有效。在这个舞台上节省的每一个小时,您都可以在更有用的舞台上花费。这是一个现实世界的考虑。
看起来 XPath 有一些好处。这里的每个答案都归结为更喜欢XPath而不是 Perl。没关系。如果您更愿意使用标准化的 XML 领域特定语言来访问您的 XML,那就试试吧!
Perl 没有提供一种简单的机制来访问深度嵌套的可选结构。
var $xml = [ { foo => 1 } ]; ## Always w/ ForceArray.
var $xml = { foo => 1 };
foo
在这两种情况下获得这里的价值可能会很棘手。XML::Simple
知道这一点,这就是您可以强制前者的原因。但是,即使使用ForceArray
,如果元素不存在,您也会抛出错误。
var $xml = { bar => [ { foo => 1 } ] };
现在,如果bar
是可选的,您将继续访问它$xml->{bar}[0]{foo}
并将@{$xml->{bar}}[0]
引发错误。无论如何,这只是perl。这与XML::Simple
恕我直言0有关。而且,我承认这XML::Simple
不利于按规范构建。显示数据,我可以使用 XML::Simple 访问它。