c#中XML解析文件出錯(cuò)解決方法
1.內(nèi)容中含有xml預(yù)定好的實(shí)體,如“<”和“&”,對(duì)xml來(lái)說(shuō)是禁止使用的,針對(duì)這種字符,解決方式是使用CDATA部件以"<![CDATA[" 標(biāo)記開(kāi)始,以"]]>"標(biāo)記結(jié)束,是CDATA內(nèi)部?jī)?nèi)容被解析器忽略。具體說(shuō)明參考《XML CDATA是什么?》。
2.內(nèi)容中含有低位非打印字符,解析時(shí)會(huì)報(bào)錯(cuò):""(十六進(jìn)制值 0x1D)是無(wú)效的字符.加載或保存XML時(shí)引發(fā)的異常.System.ArgumentException: “”(十六進(jìn)制值 0x1D)是無(wú)效的字符。
出錯(cuò)的原因是內(nèi)容中含有低位非打印字符,處理方法是對(duì)其進(jìn)行過(guò)濾,過(guò)濾方法為:
return System.Text.RegularExpressions.Regex.Replace(str,@"[\x00-\x08]|[\x0B-\x0C]|[\x0E-\x1F]";
以上兩種情況,第一種較為普遍,第二種遇到情況比較少,在面對(duì)一些用戶輸入數(shù)據(jù)時(shí)生成xml,可以對(duì)xml結(jié)點(diǎn)內(nèi)容執(zhí)行上述過(guò)濾,以保證xml文件使用者可以正確解析xml文檔。
以下是詳細(xì)解釋?zhuān)?/strong>
“”(十六進(jìn)制值 0x1D)是無(wú)效的字符
加載或保存XML時(shí)引發(fā)的異常.System.ArgumentException: “”(十六進(jìn)制值 0x1D)是無(wú)效的字符。
產(chǎn)生原因是xml文件中包含低位非打印字符造成的
處理方法:在產(chǎn)生xml文件的時(shí)候,過(guò)濾低位非打印字符
把一個(gè)字符串中的 低序位 ASCII 字符 替換成 &#x 字符
轉(zhuǎn)換 ASCII 0 - 8 -> � - 
轉(zhuǎn)換 ASCII 11 - 12 ->  - 
轉(zhuǎn)換 ASCII 14 - 31 ->  - 
簡(jiǎn)單的處理方法
return System.Text.RegularExpressions.Regex.Replace(HttpUtility.HtmlEncode(str),@"[\x00-\x08]|[\x0B-\x0C]|[\x0E-\x1F]", "");
======================================================================================================================================================
復(fù)雜處理
獲取xml時(shí),出現(xiàn)“(十六進(jìn)制值 0x1F)是無(wú)效的字符之類(lèi)Xml異常的解決辦法2008-12-19 10:44最近做新聞采集器,需要獲取很多站點(diǎn)的xml,加載個(gè)別站點(diǎn)經(jīng)常出現(xiàn)“(十六進(jìn)制值 0x1F)是無(wú)效的字符”問(wèn)題,百思不的其解。對(duì)于問(wèn)題站點(diǎn)xml的處理,開(kāi)始的思路是既然直接用 XmlDocument對(duì)象的Load()方法不行,就用LoadXML() ,用HttpWebRequest 獲取url讀到流里再轉(zhuǎn)為xml,中間可以加一些非有效字符的過(guò)濾處理,但仍然無(wú)效,僅僅解決了請(qǐng)求超時(shí)的問(wèn)題...
問(wèn)題擱置了1周后,終于在今天解決了。
其實(shí)很簡(jiǎn)單,只加一條語(yǔ)句就搞定了
XmlDocument doc = new XmlDocument();
doc.Normalize();
// 摘要:
// 將此 XmlNode 下子樹(shù)完全深度中的所有 XmlText 節(jié)點(diǎn)都轉(zhuǎn)換成“正常”形式,在這種形式中只有標(biāo)記(即標(biāo)記、注釋、處理指令、CDATA
// 節(jié)和實(shí)體引用)分隔 XmlText 節(jié)點(diǎn),也就是說(shuō),沒(méi)有相鄰的 XmlText 節(jié)點(diǎn)。
以下是轉(zhuǎn)一位仁兄的貼:
最近碰到一個(gè)問(wèn)題,我的一個(gè)把數(shù)據(jù)庫(kù)中記錄的信息暴露出來(lái)的Web Service調(diào)用時(shí)候出問(wèn)題了。報(bào)下面的錯(cuò)誤信息:
System.InvalidOperationException was unhandled Message="XML 文檔(1, 823)中有錯(cuò)誤。" Source="System.Xml" Message="“”(十六進(jìn)制值 0x0E)是無(wú)效的字符。 行 1,位置 823。" Source="System.Xml"
當(dāng)這個(gè)錯(cuò)誤發(fā)生時(shí),Web Service 服務(wù)器端不會(huì)有任何錯(cuò)誤,而調(diào)用這個(gè) Web Service 的客戶端則會(huì)報(bào)上述錯(cuò)誤。
是何原因?qū)е碌倪@個(gè)問(wèn)題呢?
答案很簡(jiǎn)單,是WEB Service 暴露的XML文檔中存在低序位非打印 ASCII 字符所致。
我們查看 Web Service 返回的XML 文檔文檔中,會(huì)有下面的XML文檔節(jié):其中的 就是低序位 ASCII 字符。 對(duì)應(yīng)的字符如后:
<Value> 在神奇天地裏誰(shuí)叱咤風(fēng)雨</Value>
會(huì)導(dǎo)致這些問(wèn)題的 低序位非打印 ASCII 字符包含以下字符:
#x0 - #x8 (ASCII 0 - 8)
#xB - #xC (ASCII 11 - 12)
#xE - #x1F (ASCII 14 - 31)
下面就是一個(gè)簡(jiǎn)單演示這個(gè)問(wèn)題的控制臺(tái)程序,
為了簡(jiǎn)單起見(jiàn),這里沒(méi)有建立 WebService, 而是把一個(gè)類(lèi)XML序列化存儲(chǔ)到文件,然后再把這個(gè)文件反序列化讀取出來(lái):
其中的這個(gè)類(lèi)的Value值中,放了一個(gè)低序位非打印 ASCII 字符。
執(zhí)行這個(gè)控制臺(tái)程序,就會(huì)報(bào)異常?!癤ML 文檔(3, 12)中有錯(cuò)誤。”
using System; using System.Xml.Serialization; using System.IO; using System.Text; using System.Globalization; namespace TextSerialize { [Serializable] public class MyClass { public string Value { get; set; } } class Program { static void Main(string[] args) { string fileName = "d:\\1.txt"; MyClass c = new MyClass(); c.Value = string.Format("在神奇{0}天地裏誰(shuí)叱咤風(fēng)雨", Convert.ToChar(14)); SaveAsXML(c, fileName, Encoding.UTF8); object o = ConvertFileToObject(fileName, typeof(MyClass), Encoding.UTF8); MyClass d = o as MyClass; if (d != null) Console.WriteLine(d.Value); else Console.WriteLine("null"); Console.ReadLine(); } /// <summary> /// 序列化 /// </summary> /// <param name="objectToConvert"></param> /// <param name="path"></param> /// <param name="encoding"></param> public static void SaveAsXML(object objectToConvert, string path, Encoding encoding) { if (objectToConvert != null) { Type t = objectToConvert.GetType(); XmlSerializer ser = new XmlSerializer(t); using (StreamWriter writer = new StreamWriter(path, false, encoding)) { ser.Serialize(writer, objectToConvert); writer.Close(); } } } /// <summary> /// 反序列化 /// </summary> /// <param name="path"></param> /// <param name="objectType"></param> /// <param name="encoding"></param> /// <returns></returns> public static object ConvertFileToObject(string path, Type objectType, Encoding encoding) { object convertedObject = null; if (!string.IsNullOrEmpty(path)) { XmlSerializer ser = new XmlSerializer(objectType); using (StreamReader reader = new StreamReader(path, encoding)) { convertedObject = ser.Deserialize(reader); reader.Close(); } } return convertedObject; } } }
上面提到的Web Service 的那個(gè)問(wèn)題,跟這個(gè)演示程序是一樣的。
我們需要被序列化的內(nèi)容中,存在 低序位非打印 ASCII 字符 時(shí), .net 會(huì)給我們正常序列化, 會(huì)自動(dòng)把 低序位非打印 ASCII 字符 轉(zhuǎn)換成 &#x 編碼的字符(這個(gè)XML規(guī)范中要求這么做的)。
但是,反序列化時(shí)候,如果需要反序列化的內(nèi)容如果存在 &#x 編碼的字符(映射到低序位非打印 ASCII 字符),則反序列化就會(huì)出錯(cuò)。
如果解決這個(gè)問(wèn)題呢?
當(dāng)然,最徹底的解決方法是修改反序列化的代碼,讓這些字符不會(huì)出錯(cuò)。但這個(gè)東西很多時(shí)候不歸我們控制。這個(gè)方案不可行。
下一個(gè)方案就是剔除這些搗亂的字符。
我這里要給出的方案,是對(duì)這些字符序列化時(shí)作一次預(yù)處理,反序列化時(shí),作一次反向處理。
這里為了演示的更有意義,我這里處理邏輯就是把 低序位非打印 ASCII 字符 轉(zhuǎn)換成 &#x 編碼的字符 ,和把&#x 編碼的字符 轉(zhuǎn)換成 低序位非打印 ASCII 字符。
這樣就可以使用我這里提供的函數(shù),實(shí)現(xiàn)更多的處理邏輯。這兩個(gè)函數(shù)的代碼如下:
/// <summary> /// 把一個(gè)字符串中的 低序位 ASCII 字符 替換成 &#x 字符 /// 轉(zhuǎn)換 ASCII 0 - 8 -> � -  /// 轉(zhuǎn)換 ASCII 11 - 12 ->  -  /// 轉(zhuǎn)換 ASCII 14 - 31 ->  -  /// </summary> /// <param name="tmp"></param> /// <returns></returns> public static string ReplaceLowOrderASCIICharacters(string tmp) { StringBuilder info = new StringBuilder(); foreach (char cc in tmp) { int ss = (int)cc; if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32))) info.AppendFormat("&#x{0:X};", ss); else info.Append(cc); } return info.ToString(); } /// <summary> /// 把一個(gè)字符串中的下列字符替換成 低序位 ASCII 字符 /// 轉(zhuǎn)換 � -  -> ASCII 0 - 8 /// 轉(zhuǎn)換  -  -> ASCII 11 - 12 /// 轉(zhuǎn)換  -  -> ASCII 14 - 31 /// </summary> /// <param name="input"></param> /// <returns></returns> public static string GetLowOrderASCIICharacters(string input) { if (string.IsNullOrEmpty(input)) return string.Empty; int pos, startIndex = 0, len = input.Length; if (len <= 4) return input; StringBuilder result = new StringBuilder(); while ((pos = input.IndexOf("&#x", startIndex)) >= 0) { bool needReplace = false; string rOldV = string.Empty, rNewV = string.Empty; int le = (len - pos < 6) ? len - pos : 6; int p = input.IndexOf(";", pos, le); if (p >= 0) { rOldV = input.Substring(pos, p - pos + 1); // 計(jì)算 對(duì)應(yīng)的低位字符 short ss; if (short.TryParse(rOldV.Substring(3, p - pos - 3), NumberStyles.AllowHexSpecifier, null, out ss)) { if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32))) { needReplace = true; rNewV = Convert.ToChar(ss).ToString(); } } pos = p + 1; } else pos += le; string part = input.Substring(startIndex, pos - startIndex); if (needReplace) result.Append(part.Replace(rOldV, rNewV)); else result.Append(part); startIndex = pos; } result.Append(input.Substring(startIndex)); return result.ToString(); }
這樣,我們這個(gè)演示程序的 Main 函數(shù)修改為下面的代碼,也不會(huì)有任何錯(cuò)誤發(fā)生。
static void Main(string[] args) { Console.WriteLine(GetLowOrderASCIICharacters("123456񐀀")); Console.WriteLine(GetLowOrderASCIICharacters("123456")); Console.WriteLine(GetLowOrderASCIICharacters("")); Console.WriteLine(GetLowOrderASCIICharacters("0123 456789")); Console.WriteLine(GetLowOrderASCIICharacters("\f")); Console.WriteLine(GetLowOrderASCIICharacters(" =-1")); Console.WriteLine(GetLowOrderASCIICharacters(" ")); Console.WriteLine(GetLowOrderASCIICharacters(" ")); string fileName = "d:\\1.txt"; MyClass c = new MyClass(); c.Value = string.Format("在神奇{0}天地裏誰(shuí)叱咤風(fēng)雨", Convert.ToChar(14)); c.Value = ReplaceLowOrderASCIICharacters(c.Value); SaveAsXML(c, fileName, Encoding.UTF8); object o = ConvertFileToObject(fileName, typeof(MyClass), Encoding.UTF8); MyClass d = o as MyClass; if (d != null) { d.Value = GetLowOrderASCIICharacters(d.Value); Console.WriteLine(d.Value); } else Console.WriteLine("null"); Console.ReadLine(); }
相關(guān)文章
Unity實(shí)現(xiàn)簡(jiǎn)單虛擬搖桿
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)簡(jiǎn)單虛擬搖桿,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04WinForm實(shí)現(xiàn)程序一段時(shí)間不運(yùn)行自動(dòng)關(guān)閉的方法
這篇文章主要介紹了WinForm實(shí)現(xiàn)程序一段時(shí)間不運(yùn)行自動(dòng)關(guān)閉的方法,涉及WinForm計(jì)時(shí)器及進(jìn)程操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09C#面向?qū)ο筇卣鞯木唧w實(shí)現(xiàn)及作用詳解
所有的面相對(duì)象思想,歸根結(jié)底是為了簡(jiǎn)化代碼,減少代碼量,構(gòu)建更符合現(xiàn)實(shí)生活邏輯的程序代碼,從而減輕程序員的負(fù)擔(dān)。不能一味地或者說(shuō)刻意地去使用面相對(duì)象的思想而忽略了程序所實(shí)現(xiàn)的功能或者框架,要根據(jù)實(shí)際情況2013-10-10C#三種方法獲取文件的Content-Type(MIME Type)
這篇文章介紹了C#獲取文件Content-Type(MIME Type)的三種方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01淺析.NET中AsyncLocal的實(shí)現(xiàn)原理
這篇文章主要為大家詳細(xì)介紹了.NET中AsyncLocal的具體實(shí)現(xiàn)原理,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,如果有講得不清晰或不準(zhǔn)確的地方,還望指出2023-08-08Unity實(shí)現(xiàn)移動(dòng)端手勢(shì)解鎖功能
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)移動(dòng)端手勢(shì)解鎖功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07