[VB/C#] System.Xml.XmlException 無効な文字の対策
VB/C# で XDocument.Load
や Parse
メソッドを呼んだときに「型 'System.Xml.XmlException’ のハンドルされていない例外が System.Xml.dll で発生しました」・「追加情報:’ ' (16 進数値 0xXX) は無効な文字です。 行 XX、位置 XX。」のような例外を避けるための話です。
GitHub など有名な開発者向けのサービスでも、このような例外を発生させる XML を出力していることは、よくあり、悩まされます。
原因
XML 内に改行やタブ以外の制御文字などが含まれているとこのような例外が発生します。ちなみに、IE で XML を表示しようとしてもエラーになります(Firefox などでは普通に表示できます)。
XML で有効な文字は次の通りのようです。
- #x9
- #xA
- #xD
- #x20-#xD7FF
- #xE000-#xFFFD
- #x10000-#x10FFFF
関連ページ: PRB: Parsing XML Containing Invalid Character May Raise ArgumentException
対策
読めることを優先し、これ以外の文字は削除してから XDocument.Parse
メソッドを呼ぶようにします。もし、制御文字が重要な場合は、HTML エンコードするように書き換えてください。
Sanitize 関数が今回用意した、文字列から XML で無効な文字列を削除して返す関数です。
Visual Basic (VB.NET)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sub Main() | |
Dim url = "http://example.jp/xml" | |
Dim client = New WebClient With {.Encoding = Text.Encoding.UTF8} | |
Dim xml = client.DownloadString(url) | |
Dim d = XDocument.Parse(Sanitize(xml)) | |
End Sub | |
Private Function Sanitize(xml As String) As String | |
Dim sb = New Text.StringBuilder | |
For Each c In xml | |
Dim code = AscW(c) | |
If code = &H9 OrElse | |
code = &HA OrElse | |
code = &HD OrElse | |
(&H20 <= code AndAlso code <= &HD7FF) OrElse | |
(&HE000 <= code AndAlso code <= &HFFFD) OrElse | |
(&H10000 <= code AndAlso code <= &H10FFFF) Then | |
sb.Append(c) | |
End If | |
Next | |
Return sb.ToString | |
End Function |
C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static void Main(string[] args) | |
{ | |
var url = "http://example.jp/xml"; | |
var client = new WebClient() { Encoding = Encoding.UTF8 }; | |
var xml = client.DownloadString(url); | |
var d = XDocument.Parse(Sanitize(xml)); | |
} | |
private static string Sanitize(string xml) | |
{ | |
var sb = new System.Text.StringBuilder(); | |
foreach (var c in xml) | |
{ | |
var code = (int)c; | |
if (code == 0x9 || | |
code == 0xa || | |
code == 0xd || | |
(0x20 <= code && code <= 0xd7ff) || | |
(0xe000 <= code && code <= 0xfffd) || | |
(0x10000 <= code && code <= 0x10ffff)) | |
{ | |
sb.Append(c); | |
} | |
} | |
return sb.ToString(); | |
} |