为什么XML ::简单劝阻?

Sob*_*que 55 xml perl xml-simple

来自以下文件XML::Simple:

不鼓励在新代码中使用此模块.其他模块可用,提供更直接和一致的接口.特别强烈建议使用XML :: LibXML.

该模块的主要问题是大量选项以及这些选项交互的任意方式 - 通常会产生意外结果.

有人可以为我澄清一下这主要原因是什么?

Sob*_*que 54

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

毫无疑问,您可以从perldata两个关键数据结构中了解到的是hasharray.

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

而且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>
Run Code Online (Sandbox Code Playgroud)

这 - 解析时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'
                                               }
                                             ]
                          }
        };
Run Code Online (Sandbox Code Playgroud)

注意 - 现在你有一些parent- 只是匿名哈希,但another_node你有一个匿名哈希数组.

所以为了访问以下内容child:

my $child = $xml -> {parent} -> {child} -> {content};
Run Code Online (Sandbox Code Playgroud)

请注意你有一个'子'节点,它下面有一个'内容'节点,这不是因为它是......内容.

但要访问第一个another_child元素下面的内容:

 my $another_child = $xml -> {another_node} -> {another_child} -> [0] -> {content};
Run Code Online (Sandbox Code Playgroud)

请注意 - 由于具有多个<another_node>元素,XML已被解析为数组,而不是单个数组.(如果你确实有一个content在它下面调用的元素,那么你最终会得到其他东西).你可以通过使用来改变它,ForceArray但最后你会得到一个哈希数组哈希数组的哈希值 - 尽管它在处理子元素时至少是一致的.编辑:注意,下面的讨论 - 这是一个错误的默认值,而不是XML :: Simple的缺陷.

你应该设置:

ForceArray => 1, KeyAttr => [], ForceContent => 1
Run Code Online (Sandbox Code Playgroud)

如果您将此应用于上述XML,则会得到:

$VAR1 = {
          'another_node' => [
                            {
                              'another_child' => [
                                                 {
                                                   'some_att' => 'a value'
                                                 },
                                                 {
                                                   'different_att' => 'different_value',
                                                   'content' => 'more content'
                                                 }
                                               ]
                            }
                          ],
          'parent' => [
                      {
                        'child' => [
                                   {
                                     'att' => 'some_att',
                                     'content' => 'content'
                                   }
                                 ]
                      }
                    ]
        };
Run Code Online (Sandbox Code Playgroud)

这将为您提供一致性,因为您将不再具有与多节点不同的单节点元素处理.

但你还是:

  • 有一个5参考深度树来获取值.

例如:

print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};
Run Code Online (Sandbox Code Playgroud)

你还有contentchild散列元素处理,如果他们的属性,而且由于哈希是无序的,你根本无法重建的投入.所以基本上,你必须解析它,然后运行它Dumper来找出你需要看的地方.

但是通过xpath查询,您可以使用以下命令获取该节点:

findnodes("/xml/parent/child"); 
Run Code Online (Sandbox Code Playgroud)

你没有得到的XML::Simple东西XML::Twig(我猜想XML::LibXML但我知道的不太好):

  • xpath支持.xpath是表示节点路径的XML方式.所以你可以在上面找到一个节点get_xpath('//child').您甚至可以使用 - 中的属性,xpath这样get_xpath('//another_child[@different_att]')可以精确选择您想要的属性.(你也可以迭代比赛).
  • 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};
Run Code Online (Sandbox Code Playgroud)

比.

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');
Run Code Online (Sandbox Code Playgroud)

  • IMO在很大程度上归结为ForceArray应该默认为1(并且在不破坏大多数现有用途的情况下无法更改).如果XML :: Simple满足您的需求,则没有理由不使用它. (7认同)
  • @Sobrique:我开始编辑你的解决方案,但是当我到达最后一段和列表时我不得不放弃.你声明的目的是解释为什么`XML :: Simple`是一个糟糕的选择,但你最终为`XML :: Twig`编写了粉丝邮件.如果你想超越解释`XML :: Simple`的问题那么你需要考虑的不仅仅是`XML :: Twig`和`XML :: LibXML`,我不相信这是一个地方这样的扩展分析 (5认同)
  • _Sadly它不是默认安装.如果"默认安装"你的意思是核心模块,那么是的,我同意你的看法.但是如果你的意思是捆绑了Perl发行版,那么至少从[2014年5月]开始,Strawberry Perl已经预先安装了XML模块(XML :: LibXML,XML :: Parser,XML :: Twig等)(http:/ /strawberryperl.com/release-notes/5.20.0.1-32bit.html),也许更长. (4认同)
  • 由于我不提供"不做X"而没有提供合适的替代品,我试图提供一些积极的理由来改变.理想情况下,协助商业案例.我是XML :: Twig的粉丝.我认为如果他们"简单地"从核心中删除XML :: simple,那将是一个很好的替代品.尤其是因为"简化"允许您保持向后兼容性.这有点偏离我所知道的意见 - 还有很多其他选择是好的. (2认同)

ike*_*ami 32

XML :: 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'
Run Code Online (Sandbox Code Playgroud)

这意味着您必须执行各种检查以查看您实际获得的内容.但这种复杂性使得开发人员做出了非常糟糕的假设.

制作更常规树的选项不足

您可以使用以下选项创建更常规的树:

ForceArray => 1, KeyAttr => [], ForceContent => 1
Run Code Online (Sandbox Code Playgroud)

但即使有这些选项,仍然需要进行许多检查才能从树中提取信息.例如,/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} };
}
Run Code Online (Sandbox Code Playgroud)

在另一个解析器中,可以使用以下内容:

my @eles = $doc->findnodes('/root/eles/ele');
Run Code Online (Sandbox Code Playgroud)

XML :: Simple带来了许多限制,它缺乏共同的功能

  • 它对于生成XML完全没用.即使有ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1,也有太多无法控制的细节.

  • 它不保留具有不同名称的子项的相对顺序.

  • 它有限(使用XML :: SAX后端)或没有(使用XML :: Parser后端)支持名称空间和名称空间前缀.

  • 它不能将文本和元素都作为子元素处理(这意味着它无法处理XHTML等).

  • 一些后端(例如XML :: Parser)无法处理不基于ASCII的编码(例如UTF-16le).

  • 元素不能具有子元素和具有相同名称的属性.

  • 它无法创建带注释的XML文档.

忽略前面提到的主要问题,XML :: Simple仍然可以使用这些限制.但是,为什么要检查XML :: Simple是否可以处理您的文档格式以及以后需要切换到另一个解析器的风险呢?您可以从一开始就为所有文档使用更好的解析器.

其他一些解析器不仅不会受到这些限制,而且还提供了许多其他有用的功能.以下是XML :: Simple不具备的一些功能:

  • 速度.XML :: Simple非常慢,特别是如果您使用XML :: Parser之外的后端.我说的是比其他解析器慢几个数量级.

  • XPath选择器或类似的.

  • 支持超大文档.

  • 支持漂亮的打印.

XML :: Simple是否有用?

XML :: Simple最简单的唯一格式是没有元素是可选的格式.我有过无数XML格式的经验,而且我从未遇到过这样的格式.

仅仅这种脆弱性和复杂性就足以保证远离XML :: Simple,但还有其他原因.

备择方案

我使用XML :: LibXML.它是一个非常快速,功能齐全的解析器.如果我需要处理不适合内存的文档,我会使用XML :: LibXML :: Reader(及其copyCurrentNode(1))或XML :: Twig(使用twig_roots).