从XML字符串中删除非法字符

3

我有一个包含一些XML的字符串。例如:

<foo>
    <bar>this is < than this</bar>
</foo>

我需要在将其加载到XmlDocument之前从中删除非法字符。 有什么想法吗?谢谢。
4个回答

5

我有一个包含一些Xml的字符串。

不,你没有。你有一些XML类似的文本,但它不是格式良好的。一旦这些文本都被粘在一起,找到特殊字符就很困难了。虽然你可以尝试查找"<"或">",但这些字符可能出现在任何地方。我的建议是退回一步,看看该字符串来自哪里。更改代码以处理特殊字符。

如果没有其他选择,我可能会暂时忽略XML工具(因为当你尝试给它们提供该字符串时,它们会抛出异常),并对特殊字符进行某种运行计数(奇数/偶数用于引号)。例如,一旦你遇到了"<",你就不能再遇到另一个,直到你遇到">"。不幸的是,你不能在属性中使用"<"等字符,因此我不知道你将如何处理<foo p1="a<a">,但至少你可以修复<foo>a<A</foo>。(假设他们永远不会在标签名称中放置"<",那么遇到第二个意味着你需要回退并转义第一个。)一旦你遇到了">",你就不能再遇到另一个。等等。我很同情你。


我明白你的意思,如果这是一个选项,我早就已经做了。 - mat-mcloughlin
所以你没有编写生成字符串的代码?你与另一个应用程序有某种契约,它应该提供给你XML,但是没有提供吗?坚持要求。你正在为自己设置一个非常困难的任务。例如,考虑 <foo p1="ha>ha" /> - Kate Gregory
没有这样的事情,我正在考虑在到达此阶段之前解析文本的可能性,但不解释应用程序的复杂性,这是去除非法字符的最佳位置之一。相信我...无论哪种方式,我知道这都会很麻烦。 - mat-mcloughlin
1
我能理解这里的OP,Kate。我曾经不得不处理来自一家大型知名硬件和软件公司的数据,据说是CSV文件。数据是逗号分隔和引号限定的,但包括在数据项中的引号和逗号。它已经很难通过肉眼解析,更不用说编写一个可以自动处理它的解析器了。由于供应商(他们是谁)不会更改格式,因此我们必须尽最大努力使其正常工作。 - ZombieSheep

1

我认为在这里最好的做法是对你可能看到的内容进行智能猜测,尽力处理它们。最重要的是确保如果你的规则失效,不会损坏其他数据——通常情况下优雅地中止而不执行任何操作并通知管理员是最理想的,但也是你可得到的最好的结果。

在你提供的例子中,数据字符串中的“<”后面似乎有一个空格,而组成标记的“<”则没有。你能利用这一点吗?

按我的经验,当处理与其规范不符的文件时,你必须从给出的细节入手,并祈求任何你选择的神祇不要让事情变得更糟。

抱歉。 ;)

编辑--

我又想到了一件事...... 你正在处理的数据是否具有严格预定义的格式?例如,它会在标记中具有可选参数吗?如果没有,你可以通过使用模式来达到非常狡猾的效果(并使经验丰富的开发人员略微哭泣)。

例如-如果你知道你总是会得到这样的标记

<myData>
  <MyFirstTag>Hello, I contain illegal < data</MyFirstTag>
  <moreData>and I am just plain <B>stupid</B></moreData>
</myData>

你可以尝试使用一些已知且唯一的字符串(例如GUID)对字段定义进行标记化处理。

knownstring1
  knownstring2Hello, I contain illegal < dataendknownstring2
  knownstring3and I am just plain <B>stupid</B>endknownstring3
endknownstring1

然后您可以对非法字符进行替换,然后将标签放回准备导入到XMLDocument中。

我知道,这让我也感到不安,但有时您所获得的数据需要您采用肮脏的技巧。


那个,可能会起作用...这可能比那更棘手,但我认为你给了我一些可以使用的东西。明天早上当我头脑更清醒时,我将尝试。谢谢。 - mat-mcloughlin
PS. 我将会把这段可爱的代码从其他开发人员中尽可能地抽象出来。它可以成为我们的秘密。 - mat-mcloughlin

1

这是在使用标记时非常常见的情况,无论是以何种方式传递给您。有两种可能性:

1)标记是由有缺陷的代码生成的,您可能有或没有访问权限。您可能会发现坏点是重复和可预测的,您可以通过自己的代码(例如正则表达式等)来减轻问题。如果您能够修复生成代码,那当然要修复。

2)标记是由不知道/不关心自己在做什么的人生成的。这是一个人的问题。不要试图用代码来解决它。您必须通过与正在执行此操作的人交谈并以某种方式处理政治问题来处理它。看看好的一面,也许您可以让您的老板来解决它。


0
这是一个用来修复你所拥有的 XML 中错误的工具:
#!/usr/bin/env perl

# Fixes unescaped "<" and "&" in between tags.

use strict;
use warnings;

use Encode qw( encode decode );

sub fix_xml {
    my ($broken_xml) = @_;

    my $enc;
    if    ( $_[0] =~ /^\xEF\xBB\xBF/ ) { $enc = 'UTF-8';    }
    elsif ( $_[0] =~ /^\xFF\xFE/     ) { $enc = 'UTF-16le'; }
    elsif ( $_[0] =~ /^\xFE\xFF/     ) { $enc = 'UTF-16be'; }
    elsif (substr($_[0], 0, 100) =~ /^[^>]* encoding="([^"]+)"/) { $enc = $1; }
    else                               { $enc = 'UTF-8';    }

    $broken_xml = decode($enc, $_[0], Encode::FB_CROAK | Encode::LEAVE_SRC);

    my $name   = qr/(?:\w+:)?\w+/x;
    my $value  = qr/(?: '[^']+' | "[^"]+" )/x;
    my $s      = qr/\s/x;
    my $attrib = qr/$name $s* = $s* $value/x;

    my $fixed_xml = '';
    for ($broken_xml) {
        /\G \z /xcg && last;

        /\G ( (?: [^<&]+ | &\#?\w+; )+               ) /xscg && do { $fixed_xml .= $1; redo };  # Text
        /\G ( < $name (?: $s+ $attrib )* $s* \/? >   ) /xscg && do { $fixed_xml .= $1; redo };  # Start or empty tag
        /\G ( <\/ $name $s* >                        ) /xscg && do { $fixed_xml .= $1; redo };  # End tag
        /\G ( <!-- (?:(?! -- ).)* -->                ) /xscg && do { $fixed_xml .= $1; redo };  # Comment
        /\G ( <!\[CDATA\[ (?:(?! \]\]> ).)* \]\]>    ) /xscg && do { $fixed_xml .= $1; redo };  # CDATA
        /\G ( <? $s* $name (?: $s+ $attrib )* $s* ?> ) /xscg && do { $fixed_xml .= $1; redo };  # Decl

        # Something illegal!
        /\G ( < ) /xscg && do { $fixed_xml .= "&#lt;";  redo };  # Unescaped "<"
        /\G ( & ) /xscg && do { $fixed_xml .= "&#amp;"; redo };  # Unescaped "&"

        die("Don't know how to fix character at position " . pos() . "\n");
    }

    return encode($enc, $fixed_xml);
}

die("usage: $0 file.xml") if !@ARGV || $ARGV[0] eq '/?' || $ARGV[0] eq '-h' || $ARGV[0] eq '--help';

my $broken_xml;
{
    open(my $fh, '<', $ARGV[0])
       or die("Can't open \"$ARGV[0]\": $!\n");
    binmode($fh);
    local $/;
    $broken_xml = <$fh>;
}

binmode(STDOUT);
print fix_xml($broken_xml);

它不能检测所有问题(例如属性中未转义的"&"),但它可以检测并修复您所询问的问题。

未经测试。


Arghh Perl :S 我是个C#程序员,这可能会是一个转换的噩梦... 有可能吗? - mat-mcloughlin
@mjmcloug,为什么要转换它?C#肯定可以启动程序,或者您可以在将它们传递给您的程序之前先修复它们。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接