对含有非法字符(Invalid Character)的Xml数据进行容错处理

System.Xml在Load含有非法字符的Xml文件时出错

通过.Net的System.Xml进行Xml文件或者字符串进行载入(Load)的时候,如果Xml数据包含非法字符,会产生一个异常错误(Exception)。通常情况下这个异常是XmlException类型,某些时候是ArgumentException类型。通常的XmlException错误信息示例如下:

'', hexadecimal value 0x13, is an invalid character. Line 1, position 28.

这个问题在微软的KB文章325694 - PRB: Parsing XML Containing Invalid Character May Raise ArgumentException中进行了解释,通过Google Group: xml hexadecimal value invalid character或者Google: xml hexadecimal value invalid character可以找到很多信息。

Xml非法字符的定义

Xml规范关于字符(Characters)的定义是这样的:

Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

这个定义包含了所有的Unicode字符,但是剔除了所有代理区(Surrogate Blocks)和FFFE、FFFF。所谓代理区是Unicode定义中的特殊字符,可以这样简单理解,这些字符不能单独存在,必须和其他字符结合才构成意义。如果一个Xml数据是被正确编码后再被正确解码的话,是不应该出现这些字符的。

上述定义也等同于定义了Xml数据中的非法字符:

InvalidChar ::= [#x0-#x8] | #xB | #xC | [#xE - #x1F] | [#xD800 - #xDFFF] | #xFFFE | #xFFFF

不过,由于种种原因,很多程序的Xml输出并不是那么正确,导致很多地方出现了上述的非法字符。这些字符有时候会彻底破坏Xml数据的结构,比如说出现在Tag名称里面,不过大部分时候,如果把他们替换成为空格,也不影响数据的基本正确性,例如出现在某个描述属性值里面。

容错处理

将包含Xml非法字符的Xml数据,进行非法字符替换,再重新尝试载入(Load),就等于是进行了容错处理。下面代码利用正则表达式对含有非法字符的Xml字符串进行了替换。

Public Class XmlTextConvert
Private Shared RegexInvalidXmlChars As Regex = New Regex( _
"[\u0000-\u0008\u000B\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]", RegexOptions.Compiled)

Public Shared Function FixInvalidChars(ByVal txt As String, ByVal replacement As String) As String
Return RegexInvalidXmlChars.Replace(txt, replacement)
End Function

Public Shared Function FixInvalidChars(ByVal txt As String) As String
Return FixInvalidChars(txt, " ")
End Function
End Class

要注意,前文定义中,InvalidChar中的竖线(|)是表示或者的意思,这是是定义语法,可不是正则表达式的语法。写正则表达式的时候,千万别把竖线写进去,那样会把竖线符号也一起过滤掉了。

如果需要进行容错处理的对象是Xml文件或者Url,问题稍微复杂一点。需要指出的是,很多网站的Rss输出的确存在一定几率出现非法字符的问题。对Xml数据流(Stream)进行容错,要事先用正确的解码将其转换成为字符串,然后再进行非法字符过滤。取得字符编码(Encoding)的代码如下:

Private Shared Function GetXmlEncoding(ByVal stm As Stream) As Encoding
Dim pos As Long = stm.Position
Dim xrd As XmlTextReader = New XmlTextReader(stm)
xrd.Read()
Dim xdoc As XmlDocument = New XmlDocument
Dim hnd As XmlNode, enc As Encoding = xrd.Encoding
Do While Not xrd.EOF
Try : hnd = xdoc.ReadNode(xrd)
Catch ex As XmlException : Exit Do
End Try
If hnd.NodeType = XmlNodeType.XmlDeclaration Then
Dim xdecl As XmlDeclaration = CType(hnd, XmlDeclaration)
enc = Encoding.GetEncoding(xdecl.Encoding)
Exit Do
End If
Loop
stm.Position = pos
Return enc
End Function

对Stream进行非法字符过滤的代码如下:

Private Shared Function OpenXmlFixChars(ByVal stm As Stream) As XmlDocument
Dim enc As Encoding = GetXmlEncoding(stm)
If enc Is Nothing Then : Return Nothing : End If
Dim frd As StreamReader = New StreamReader(stm, enc)
Dim xml As String = frd.ReadToEnd
Dim xmlfix As String = XmlTextConvert.FixInvalidChars(xml)
Dim xdoc As XmlDocument = New XmlDocument
Try : xdoc.LoadXml(xmlfix)
Catch exp As XmlException : Return Nothing
End Try
Return xdoc
End Function

本问题解决得到了TIPS-Remove Invalid Characters From XML Document一文的极大帮助。

作者: 杰棍 [Jegwon]

波波坡原创文章 链接:http://www.bobopo.com/article/code/xml_invalid_character.htm

标签:

关键词: Xml, 非法字符, Invalid Character, 载入, Load, 异常, XmlDocument, .Net Framework, DotNet, VB.Net, 容错处理, 字符过滤, 正则表达式

创建日期: 2008-03-04

修改日期: 2008-07-17

文库 微博 博客 作品 首页