被一個 XML 讀取問題卡住大半天,寫篇筆記留念。

我有個 Coding4Fun 電子書製作工具,將 XHTML 範本檔案內嵌成資源(Embedded Resource),再用 GetEmbResString() 讀取範本 XML 交給 XDocument.Parse() 轉成 XML 物件操作:

static string GetEmbResString(string name) => 
    Encoding.UTF8.GetString(GetEmbResBytes(name));


static byte[] GetEmbResBytes(string name)
{
    using (var ms = new MemoryStream())
    {
        GetEmbResStream(name).CopyTo(ms);
        return ms.ToArray();
    }
}

static Stream GetEmbResStream(string name) => 
    typeof(EPubMaker).Assembly.GetManifestResourceStream(
        typeof(EPubMaker).FullName.Replace(nameof(EPubMaker), "Templates." + name));
        
//... 讀取 XML 範本轉為 XDocument ...
var template = GetEmbString("template.html");
XDocument.Parse(template);

該 XML 範本在其他地方有用過,內容、格式應該都符合規範。不料,系統噴出 System.Xml.XmlException: 'Data at the root level is invalid. Line 1, position 1.' 錯誤!

有趣的是,我如果換個寫法,改取 Stream 再配合 XDocument.Load 就過關了:

var template = GetEmbResStream("template.html");
XDocument.Load(template);

經過一番研究,我終於搞懂是怎麼一回事了(結論是自己學藝不精、江湖經驗不足),並知道怎麼重現它:

如以上範例,我做了一個超簡單的 XML 檔案,跑 XDocument.Load(XML檔)、XDocument.Parse(File.ReadAllText(XML檔)) 都沒問題;但如果先 File.ReadAllBytes(XML檔) 讀成 byte[] 再 Encoding.UTF8.GetString() 轉字串餵給 XDocument.Parse(),便會重現"在根層次的資料無效。行1,位置1。"錯誤。

聰明的你,想到是什麼原因了嗎?

Yes,UTF8 BOM!

用 BitConverter.ToString(byte[]) 檢視 ReadAllBytes() 讀取的二進位內容,果然在最前方看到 EF BB BF (UTF8 BOM),File.ReadAllText()、XDocument.Load() 這些處理檔案的函式都認得 BOM,而我的程式傻傻地讓它變成字串內容,引發錯誤。

再學到一些實戰經驗。

A case of UTF-8 BOM issue while processing XML with XDocument.Parse().


Comments

Be the first to post a comment

Post a comment


93 - 53 =