来自XML::Simple
的文档
不鼓励在新代码中使用此模块。还可以使用其他模块,它们提供了更直观、更一致的界面。特别是,强烈推荐使用XML::LibXML。
这个模块的主要问题是大量的选项和这些选项交互的任意方式-通常会产生意想不到的结果。
有没有人能为我解释一下这其中的主要原因是什么?
发布于 2015-10-22 03:36:44
真正的问题是,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')
在上面的代码中“找到”一个节点。您甚至可以在类似xpath
的get_xpath('//another_child[@different_att]')
中使用属性,它将准确地选择您想要的属性。(您也可以迭代匹配项)。cut
和paste
用于移动图元aroundparsefile_inplace
,以允许您通过在位编辑来修改XML
。pretty_print
选项,用于格式化XML
。twig_handlers
和purge
-允许您处理非常大的XML,而不必将其全部加载到内存中。如果你真的想让它向后兼容XML::Simple
,那就使用simplify
。它也可以广泛使用-很容易从CPAN
下载,并以可安装包的形式在许多操作系统上分发。(遗憾的是,这不是默认安装。还没有)
为了便于比较:
my $xml = XMLin( \*DATA, ForceArray => 1, KeyAttr => [], ForceContent => 1 );
print Dumper $xml;
print $xml ->{parent}->[0]->{child}->[0]->{content};
Vs.
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');
发布于 2015-10-22 12:37:19
Simple是可用的最复杂的XML解析器
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::Simple强加了许多限制,并且它缺乏共同的特性
它对于生成是完全无用的。即使有了ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1
,对于命名空间和命名空间解析器后端(例如,XML::Parser)的支持有限(使用XML::SAX后端)或不支持(使用XML::解析器后端)。controlled.
H217<的子元素和属性/code>
无法创建带注释的
文档。
忽略前面提到的主要问题,XML::Simple仍然可以在具有这些限制的情况下使用。但是,为什么要麻烦地检查XML::Simple是否可以处理您的文档格式,并冒着以后必须切换到另一个解析器的风险呢?您可以简单地从一开始就为所有文档使用更好的解析器。
其他一些解析器不仅不会让您受到这些限制,它们还提供了许多其他有用的特性。以下是XML::Simple可能没有的一些功能:
速度。XML::Simple非常慢,特别是当您使用XML::Parser之外的后端时。我说的是比其他parsers.
XPath选择器慢几个数量级,或者similar.
Support比非常大的documents.
Support慢几个数量级,打印很漂亮。
XML::Simple有用吗?
对于XML::Simple来说,唯一最简单的格式是没有可选元素的格式。我有过无数使用XML格式的经验,但从来没有遇到过这样的格式。
仅这种脆弱性和复杂性就足以保证远离XML::Simple,但还有其他原因。
替代方案
我使用XML::LibXML。它是一个速度极快、功能齐全的解析器。如果需要处理内存不能容纳的文档,我会使用XML::LibXML::Reader (及其copyCurrentNode(1))或XML::Twig (使用twig_roots)。
发布于 2015-10-23 00:23:46
我不同意文档的观点
我不同意,说XML::Simple
就是这样的..很简单。而且,对我来说,使用它一直都很容易,也很有趣。用你收到的输入来测试它。只要输入没有改变,你就是好的。抱怨使用XML::Simple
的人也抱怨使用JSON::Syck
序列化驼鹿。这些文档是错误的,因为它们考虑的是正确性而不是效率。如果你只关心以下几点,那你就不错了:
如果你正在制作一个抽象解析器,它不是由应用程序定义的,而是由规范定义的,我会使用其他的东西。我曾经在一家公司工作过,我们不得不接受300种不同的XML模式,但没有一种模式有规范。XML::Simple
轻松地完成了这项工作。其他选项将要求我们实际雇用某人来完成这项工作。每个人都认为XML是以严格的、包罗万象的格式发送的,如果您编写一个解析器,那么您就是好的。如果是这样的话,就不要使用XML::Simple
。在JSON之前,XML只是一种从一种语言到另一种语言的“转储并遍历”格式。人们实际上使用像XML::Dumper
这样的东西。实际上,没有人知道输出了什么。处理这种情况时,XML::Simple
非常棒!理智的人仍然会在没有规范的情况下转储到JSON来完成同样的事情。这就是世界的运转方式。
想要读入数据,而不用担心格式吗?想要遍历Perl结构而不是XML?Go XML::Simple
。
延伸一下..。
同样,对于大多数应用程序,JSON::Syck
足以将其转储并遍历。不过,如果你要发送给很多人,我强烈建议你不要做一个愚蠢的喷嘴,而是制定一个你可以输出的规范。但是你知道吗..。有时你会接到一个你不想和他交谈的人的电话,他想要你通常不会导出的数据。如果他们想要XML,那就再多收500美元,然后启动你的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
imho有关。而且,我承认XML::Simple
不适合按照规范进行构建。显示数据,我就可以用XML::Simple访问它。
https://stackoverflow.com/questions/33267765
复制相似问题