处理格式错误的XML

时间:2015-03-07 00:21:06

标签: xml perl

我在perl中处理格式错误的XML,这是由我无法改变的上游流程生成的(这似乎是一个常见的问题)。但是,就我所见,XML只是以一种特殊方式出现格式错误:它的属性值包含非转义符号,例如:

<tag v="< 2">

我使用带有XML::LibXML的perl进行解析,当然,这会产生解析错误。我尝试过使用recover选项,它允许我解析,但它只是在遇到第一个解析错误时停止,所以我就是这样丢失数据。

似乎我有两个一般选择:

  1. 在解析之前修复输入XML,可能使用正则表达式。
  2. 找一个更宽容的XML解析器。
  3. 我倾向于选项1,因为我想抓住XML的任何其他错误。你会推荐什么?如果#1,有人可以指导我完成正则表达式吗?

2 个答案:

答案 0 :(得分:8)

我知道这不是你想要的答案 - 但XML规范非常明确和严格。

格式错误的XML是致命的。

如果它在验证器中不起作用,那么您的代码甚至不应该尝试“修复”它,而不是您尝试并自动“修复”某些程序代码。

来自Anotated XML Specification

  

致命错误   [定义:]符合XML处理器必须检测并向应用程序报告的错误。在遇到致命错误之后,处理器可以继续处理数据以搜索进一步的错误并且可以将这样的错误报告给应用程序。为了支持纠正错误,处理器可以从文档中获得未处理的数据(具有混合的字符数据和标记),以供应用程序使用。但是,一旦检测到致命错误,处理器就不能继续正常处理(即,它不能继续以正常方式将字符数据和有关文档逻辑结构的信息传递给应用程序)。

特别是关于原因的评论:"Draconian" error-handling

  

我们希望XML能够让程序员编写可以通过Web传输并在大量桌面上执行的代码。但是,如果此代码必须包含针对各种草率最终用户实践的错误处理,则必须将其大小扩展到与Netscape Navigator或Microsoft Internet Explorer一样大小为数十兆字节的程度。打败目的。

如果您曾尝试将HTML解析器组合在一起,您将会发现为什么它需要这样 - 您最终会为边缘情况编写SO MANY处理程序,错误的标记嵌套,以及您的代码隐含标记关闭从一开始就是一团糟。

因为这是我在Stack Overflow上最喜欢的帖子 - 这是一个原因的例子:RegEx match open tags except XHTML self-contained tags

现在我感谢这并不总是一个选择,如果要求你的上游'修复你的XML'是阻力最小的路径,你可能不会来这里。但是我仍然会敦促你把它作为XML原始应用程序中的缺陷报告,并尽可能地抵制以编程方式'修复'的压力 - 因为正如你正确地想到的那样,它正在构建一个痛苦的世界,当正确的答案是“在源头修复问题”。

如果你真的被困在这条路上,你可以 - 正如Sinan Ünür所指出的那样 - 你唯一的选择是陷阱解析器失败的地方,然后检查并尝试修复。但是你找不到一个能够为你做的XML解析器,因为那个按照定义的那个解析了。

我首先建议

  • 挖出一份规格的副本,以便向任何要求你这样做的人展示。
  • 向他们指出,我们制定标准的全部原因是为了促进互操作性。
  • 因此,通过做某些故意违反标准的事情,你冒着商业风险 - 你创造的代码可能有一天会神秘地破坏,因为使用正则表达式或自动修复等东西正在构建在一系列可能不成立的假设中。
  • 这里有一个有用的概念是technical debt - 通过自动修复来解释你正在承担技术债务,因为这不是你的问题。
  • 然后问他们是否愿意接受这种风险。
  • 如果他们确实认为这是一个可接受的风险,那么只需继续使用它 - 您可能会发现它值得 - 实际上 - 忽略您的源数据看起来像 XML这样的事实并将其视为一样是纯文本 - 使用正则表达式来提取相关的数据行等。
  • 在评论中向未来的维护程序员道歉,解释由谁做出决定以及为什么做出决定。

也可能有用作参考点:Which character should not be set as values in XML file

答案 1 :(得分:7)

一个选项是捕获异常,找出它们在输入中的位置,在那里修复输入,然后重试。

以下是使用XML::Twig的快速,低效的概念验证脚本,因为我还没有弄清楚如何构建&amp;在Windows上从头开始安装libxml2。

#!/usr/bin/env perl

use strict;
use warnings;

use XML::Twig;

my $xml = q{ <tag v="< 2"/> };

while ( 1 ) {
    eval {
        my $twig = XML::Twig->new(
            twig_handlers => { tag => \&tag_handler },
        );
        $twig->parse( $xml );
        1;
    } and last;

    my $err = $@;

    my ($i) = ($err =~ /byte ([0-9]+)/)
        or die $err;

    substr($xml, $i, 1) eq '<'
        or die $err;
    $xml = substr($xml, 0, $i) . '&lt;' . substr($xml, $i + 1);
}

sub tag_handler {
    my (undef, $elt) = @_;
    print $elt->att('v'), "\n";
}

我写了更多关于这个on my blog