为什么XML ::简单“劝阻”?
从XML::Simple
的文档:
不鼓励在新代码中使用这个模块。 其他模块可以提供更直接和一致的接口。 尤其强烈build议使用XML :: LibXML。
这个模块的主要问题是大量的选项和这些选项交互的任意方式 – 通常会带来意想不到的结果。
有人能为我澄清这是什么原因?
真正的问题是, XML::Simple
主要尝试的是取XML,并将其表示为perl数据结构。
正如你毫无疑问从perldata
意识到你有两个关键的数据结构是hash
和array
。
- 数组是有序的标量。
- 哈希是无序的键值对。
而XML不是真的。 它的要素是:
- 非唯一命名(这意味着哈希不“适合”)。
- ….但在文件中被“sorting”。
- 可能有属性(你可以插入一个散列)
- 可能有内容(但可能不会,但可能是一个单一的标签)
- 可能有孩子(任何深度)
这些东西并不直接映射到可用的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已经被parsing为一个数组,它不是一个单独的数组。 (如果您确实有一个名为content
的元素,那么您最终还是有其他的东西)。 你可以通过使用ForceArray
来改变它,但是最后你会得到一个散列数组散列数组的哈希值 – 尽pipe它在处理子元素方面至less是一致的。 编辑:请注意,下面的讨论 – 这是一个错误的默认值,而不是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' } ] } ] };
这会给你一致性,因为你将不再有单节点元素处理不同的多节点。
但你仍然:
- 有一个5参考深层树得到一个值。
例如:
print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};
你仍然有content
和child
哈希元素被视为属性,因为散列是无序的,你根本无法重buildinput。 所以基本上,你必须parsing它,然后通过Dumper
运行,找出你需要看的地方。
但是使用xpath
查询,您可以在该节点上获得:
findnodes("/xml/parent/child");
你不能在XML::Simple
中得到你在XML::Twig
做的事情(我假设XML::LibXML
但我知道它不太好):
-
xpath
支持。xpath
是expression节点path的XML方式。 所以你可以通过get_xpath('//child')
在上面find一个节点。 你甚至可以在xpath
使用属性 – 比如get_xpath('//another_child[@different_att]')
,它将精确地select你想要的。 (你也可以迭代匹配)。 -
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是可用的最复杂的XMLparsing器
XML :: Simple的主要问题是生成的结构非常难以正确导航。 $ele->{ele_name}
可以返回以下任何一个(即使对于遵循相同规范的元素):
[ { att => 'val', ..., content => 'content' }, ... ] [ { att => 'val', ..., }, ... ] [ 'content', ... ] { 'id' => { att => 'val', ..., content => 'content' }, ... } { 'id' => { att => 'val', ... }, ... } { 'id' => { content => 'content' }, ... } { att => 'val', ..., content => 'content' } { att => 'val', ..., } 'content'
这意味着你必须进行各种检查,看看你实际得到了什么。 但是,这种复杂性鼓励开发者做出非常糟糕的假设。
制作更多规则树的选项不足
您可以使用以下选项来创build更常规的树:
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} }; }
在另一个parsing器中,可以使用以下内容:
my @eles = $doc->findnodes('/root/eles/ele');
XML :: Simple强加了很多限制,而且缺less常见的特性
-
生成XML是完全没有用的。 即使
ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1
,也有太多细节无法控制。 -
它不保留不同名字的孩子的相对顺序。
-
它限制了(使用XML :: SAX后端)或不使用(使用XML :: Parser后端)对名称空间和名称空间前缀的支持。
-
它不能处理包含文本和元素的元素作为子元素(这意味着它无法处理XHTML等)。
-
某些后端(例如XML :: Parser)无法处理不基于ASCII的编码(例如UTF-16le)。
-
一个元素不能有一个子元素和一个同名的属性。
-
它不能用注释创buildXML文档。
忽略之前提到的主要问题,XML :: Simple仍然可以用于这些限制。 但是,为什么要检查XML :: Simple是否可以处理你的文档格式,以后再切换到另一个parsing器? 您可以简单地从一开始就为您的所有文档使用更好的parsing器。
其他一些parsing器不但不会受到这些限制,还会提供大量其他有用的function。 以下是XML :: Simple不具备的一些function:
-
速度。 XML :: Simple非常慢,特别是如果您使用XML :: Parser以外的后端。 我说的比其他parsing器要慢几个数量级。
-
XPathselect器或类似的。
-
支持非常大的文件。
-
支持漂亮的打印。
XML :: Simple有用吗?
XML :: Simple最简单的唯一格式是没有元素是可选的。 我有无数的XML格式的经验,我从来没有遇到过这样的格式。
单单这种脆弱性和复杂性足以保证远离XML :: Simple,但还有其他的。
备择scheme
我使用XML :: LibXML。 这是一个非常快速,function全面的parsing器。 如果我需要处理不适合内存的文档,我会使用XML :: LibXML :: Reader(及其copyCurrentNode(1)
)或XML :: Twig(使用twig_roots
)。
我不同意这些文件
我会异议并说XML::Simple
就是这么简单。 而且,使用我总是很容易和愉快的。 使用您收到的input进行testing。 只要input不改变,你就很好。 同样的人抱怨使用XML::Simple
抱怨使用JSON::Syck
序列化驼鹿。 文档是错误的,因为它们考虑了正确性而不是效率。 如果你只关心以下内容,那么你很好:
- 不扔掉数据
- 构build为提供的格式而不是抽象的模式
如果你正在创build一个抽象的parsing器,而不是由应用程序定义,但是通过规范,我会使用别的东西。 我在一家公司工作过一次,我们不得不接受300个不同规格的XML,没有一个规范。 XML::Simple
很容易地完成了这项工作。 其他select将要求我们实际上雇人来完成工作。 每个人都认为XML是以僵化的方式发送的,如果你写一个parsing器,你就是好的。 如果是这种情况,请不要使用XML::Simple
。 在JSON之前,XML只是一种“从一种语言到另一种语言的转换”。 人们实际上使用了诸如XML::Dumper
类的东西。 没有人知道输出的是什么。 处理这种情况XML::Simple
是伟大的! 理智的人仍然转储到JSON没有规范完成相同的事情。 世界是如何运作的
想要读取数据,而不用担心格式? 想要遍历Perl结构而不是XML可能性? 去XML::Simple
。
通过扩展…
同样,对于大多数应用程序来说, JSON::Syck
足以转储和走路。 虽然如果你送给很多人的话,我强烈build议不要使用冲洗喷嘴,而是制作一个你输出的规格。 但是,你知道什么..有时候,你会打电话给你不想谈的人,他们想要你的数据,而你通常不会输出。 而且,你要通过JSON::Syck
的巫术来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
不适合构build规范。 向我显示数据,我可以用XML :: Simple访问它。