为什么XML ::简单劝阻?

时间:2015-10-21 19:36:44

标签: xml perl xml-simple

来自XML::Simple的文档:

  

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

     

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

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

3 个答案:

答案 0 :(得分:52)

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

毫无疑问,perldata您可以通过hasharray获得两个关键数据结构。

  • 阵列是有序的标量。
  • 哈希是无序的键值对。

XML并没有真正做到。它有以下元素:

  • 非唯一命名(这意味着哈希不会"#34;适合")。
  • ....但是被命令'在文件中。
  • 可能有属性(可以插入哈希值)
  • 可能有内容(但可能没有,但可能是一元标签)
  • 可能有孩子(任何深度)

这些东西并不直接映射到可用的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};

请注意您是如何生育孩子的?节点,内容&#39;在它下面的节点,这不是因为它的......内容。

但要访问第一个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'
                                   }
                                 ]
                      }
                    ]
        };

这将为您提供一致性,因为您将不再拥有与多节点不同的单节点元素。

但你还是:

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

例如:

print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};

您仍然将contentchild哈希元素视为属性,并且因为哈希是无序的,您根本无法重建输入。所以基本上,你必须解析它,然后通过Dumper运行它来找出你需要查看的位置。

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

findnodes("/xml/parent/child"); 

你在XML::Simple {{}}}中所做的事情(我假设为XML::LibXML,但我不太清楚):

  • xpath支持。 xpath是表示节点路径的XML方式。所以你可以找到&#39;上面的节点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};

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');

答案 1 :(得分:32)

XML :: Simple是最复杂的XML解析器

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'

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

制作更常规树的选项不足

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

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强加众多限制,并且缺少常用功能

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

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

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

  • 它无法处理同时包含text和elements作为子元素的元素(这意味着它无法处理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)。< / p>

答案 2 :(得分:4)

我不同意文档

我不同意并说XML::Simple就是那么简单。并且,对我来说,它总是很容易和愉快。使用您收到的输入进行测试。只要输入没有改变,你就会很好。抱怨使用XML::Simple的同一个人抱怨使用JSON::Syck序列化Moose。文档是错误的,因为它们考虑了效率的正确性。如果您只关心以下内容,那就很好:

  • 不丢弃数据
  • 构建为提供的格式而非抽象模式

如果您正在制作一个不是由应用程序定义的抽象解析器,而是按规范定义,我会使用其他东西。我曾在一家公司工作过一次,我们不得不接受300种不同的XML模式,其中没有一种规格。 XML::Simple轻松完成了这项工作。其他选择要求我们实际雇用某人来完成工作。每个人都认为XML是以严格的所有包含的规范格式发送的,这样如果你编写一个解析器就很好。如果是这种情况,请不要使用XML::Simple。在JSON之前,XML只是从一种语言到另一种语言的“转储和遍历”格式。人们实际上使用了XML::Dumper之类的东西。没有人真正知道输出了什么。处理这种情况XML::Simple非常棒! Sane人仍然倾向于JSON而没有规范来完成同样的事情。这就是世界的运作方式。

想要读取数据,而不是担心格式?想要遍历Perl结构而不是XML可能性?转到XML::Simple

通过扩展...

同样,对于大多数应用程序JSON::Syck足以转储并行走。虽然如果你发送给很多人,我会< em>高度建议不要使用冲洗喷嘴并制作您导出的规格。但是,你知道吗......有时候你会接到一个你不想和他说话的人打电话,而你想要的数据是你通常不会导出的。并且,你将通过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有关0。而且,我承认XML::Simple不适合建立规范。向我展示数据,我可以使用XML :: Simple访问它。