为什么XML ::简单“劝阻”?

XML::Simple的文档:

不鼓励在新代码中使用这个模块。 其他模块可以提供更直接和一致的接口。 尤其强烈build议使用XML :: LibXML。

这个模块的主要问题是大量的选项和这些选项交互的任意方式 – 通常会带来意想不到的结果。

有人能为我澄清这是什么原因?

真正的问题是, XML::Simple主要尝试的是取XML,并将其表示为perl数据结构。

正如你毫无疑问从perldata意识到你有两个关键的数据结构是hasharray

  • 数组是有序的标量。
  • 哈希是无序的键值对。

而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}; 

你仍然有contentchild哈希元素被视为属性,因为散列是无序的,你根本无法重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你想要的。 (你也可以迭代匹配)。
  • cutpaste移动元素
  • parsefile_inplace允许您使用就地编辑来修改XML
  • pretty_print选项来格式化XML
  • twig_handlerspurge – 它允许你处理真正的大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访问它。