我有一个包含一些XML的字符串。例如:
<foo>
<bar>this is < than this</bar>
</foo>
我需要在将其加载到
XmlDocument
之前从中删除非法字符。 有什么想法吗?谢谢。我有一个包含一些Xml的字符串。
不,你没有。你有一些XML类似的文本,但它不是格式良好的。一旦这些文本都被粘在一起,找到特殊字符就很困难了。虽然你可以尝试查找"<"或">",但这些字符可能出现在任何地方。我的建议是退回一步,看看该字符串来自哪里。更改代码以处理特殊字符。
如果没有其他选择,我可能会暂时忽略XML工具(因为当你尝试给它们提供该字符串时,它们会抛出异常),并对特殊字符进行某种运行计数(奇数/偶数用于引号)。例如,一旦你遇到了"<",你就不能再遇到另一个,直到你遇到">"。不幸的是,你不能在属性中使用"<"等字符,因此我不知道你将如何处理<foo p1="a<a">
,但至少你可以修复<foo>a<A</foo>
。(假设他们永远不会在标签名称中放置"<",那么遇到第二个意味着你需要回退并转义第一个。)一旦你遇到了">",你就不能再遇到另一个。等等。我很同情你。
我认为在这里最好的做法是对你可能看到的内容进行智能猜测,尽力处理它们。最重要的是确保如果你的规则失效,不会损坏其他数据——通常情况下优雅地中止而不执行任何操作并通知管理员是最理想的,但也是你可得到的最好的结果。
在你提供的例子中,数据字符串中的“<”后面似乎有一个空格,而组成标记的“<”则没有。你能利用这一点吗?
按我的经验,当处理与其规范不符的文件时,你必须从给出的细节入手,并祈求任何你选择的神祇不要让事情变得更糟。
抱歉。 ;)
编辑--
我又想到了一件事...... 你正在处理的数据是否具有严格预定义的格式?例如,它会在标记中具有可选参数吗?如果没有,你可以通过使用模式来达到非常狡猾的效果(并使经验丰富的开发人员略微哭泣)。
例如-如果你知道你总是会得到这样的标记
<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中。
我知道,这让我也感到不安,但有时您所获得的数据需要您采用肮脏的技巧。
这是在使用标记时非常常见的情况,无论是以何种方式传递给您。有两种可能性:
1)标记是由有缺陷的代码生成的,您可能有或没有访问权限。您可能会发现坏点是重复和可预测的,您可以通过自己的代码(例如正则表达式等)来减轻问题。如果您能够修复生成代码,那当然要修复。
2)标记是由不知道/不关心自己在做什么的人生成的。这是一个人的问题。不要试图用代码来解决它。您必须通过与正在执行此操作的人交谈并以某种方式处理政治问题来处理它。看看好的一面,也许您可以让您的老板来解决它。
#!/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);
它不能检测所有问题(例如属性中未转义的"&"),但它可以检测并修复您所询问的问题。
未经测试。
<foo p1="ha>ha" />
。 - Kate Gregory