处理格式错误的XML

dis*_*low 4 xml perl

我正在处理由perl生成的格式错误的XML,这是由我无法改变的上游进程生成的(这似乎是一个常见的问题).但是,据我所见,XML只有一种特殊的方式格式错误:它的属性值包含未转义的小于号,例如:

<tag v="< 2">
Run Code Online (Sandbox Code Playgroud)

我正在使用带有XML :: LibXML的 perl 进行解析,当然,这会生成解析错误.我已经尝试使用recover选项,它允许我解析,但它只是在遇到第一个解析错误时停止,所以我正在以这种方式丢失数据.

看起来我有两个一般的选择:

  1. 在解析之前修复输入XML,可能使用正则表达式.
  2. 找一个更宽容的XML解析器.

我倾向于选项1,因为我想捕获XML的任何其他错误.你会推荐什么?如果#1,有人可以引导我通过正则表达式方法吗?

Sob*_*que 8

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

格式错误的XML是致命的.

如果它在验证器中不起作用,那么你的代码甚至不应该试图"修复"它,而不是你试图自动"修复"一些程序代码.

来自Anotated XML Specification:

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

特别是关于原因的评论:"严厉"错误处理

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

如果你曾尝试为HTML编写一个解析器,你就会明白为什么它需要这样 - 你最终为边缘情况编写了很多处理程序,错误的标记嵌套,暗示标记关闭你的代码是一团糟从一开始.

而且因为它是Stack Overflow上我最喜欢的帖子 - 这是一个原因的例子:RegEx匹配开放标签,除了XHTML自包含标签

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

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

建议你先说:

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

也可能有用作参考点:不应将哪个字符设置为XML文件中的值

  • 有时(通常,我认为),这不是你的一面是不合理的.你被一个不露面的组织所提供的东西所困扰. (3认同)

Sin*_*nür 7

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

以下是一个快速,低效的概念验证脚本使用,XML::Twig因为我还没有想出如何在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";
}
Run Code Online (Sandbox Code Playgroud)

在博客上写了更多相关内容.