序列化,反序列化时低序位非打印 ASCII 字符的问题
最近碰到一个问题,我的一个把数据库中记录的信息暴露出来的Web Service调用时候出问题了。报下面的错误信息: System.InvalidOperationException was unhandled 当这个错误发生时,Web Service 服务器端不会有任何错误,而调用这个 Web Service 的客户端则会报上述错误。 <Value>在神奇天地裏誰叱咤風雨</Value> 会导致这些问题的 低序位非打印 ASCII 字符包含以下字符: 下面就是一个简单演示这个问题的控制台程序, 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}天地裏誰叱咤風雨",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 的那个问题,跟这个演示程序是一样的。 我们需要被序列化的内容中,存在 低序位非打印 ASCII 字符 时, .net 会给我们正常序列化, 会自动把 低序位非打印 ASCII 字符 转换成 &#x 编码的字符(这个XML规范中要求这么做的)。 但是,反序列化时候,如果需要反序列化的内容如果存在 &#x 编码的字符(映射到低序位非打印 ASCII 字符),则反序列化就会出错。 ? 如果解决这个问题呢? 当然,最彻底的解决方法是修改反序列化的代码,让这些字符不会出错。但这个东西很多时候不归我们控制。这个方案不可行。 下一个方案就是剔除这些捣乱的字符。 我这里要给出的方案,是对这些字符序列化时作一次预处理,反序列化时,作一次反向处理。 ? /// <summary> /// 把一个字符串中的 低序位 ASCII 字符 替换成 &#x 字符 /// 转换 ASCII 0 - 8 -> � -  /// 转换 ASCII 11 - 12 ->  -  /// 转换 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> /// 把一个字符串中的下列字符替换成 低序位 ASCII 字符 /// 转换 � -  -> ASCII 0 - 8 /// 转换  -  -> ASCII 11 - 12 /// 转换  -  -> 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); // 计算 对应的低位字符 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(); } ? 这样,我们这个演示程序的 Main 函数修改为下面的代码,也不会有任何错误发生。 ? static void Main(string[] args) { Console.WriteLine(GetLowOrderASCIICharacters("123456񐀀")); Console.WriteLine(GetLowOrderASCIICharacters("123456")); Console.WriteLine(GetLowOrderASCIICharacters("")); Console.WriteLine(GetLowOrderASCIICharacters("0123456789")); Console.WriteLine(GetLowOrderASCIICharacters("/f")); ? ? ? 小结 低序位非打印 ASCII 字符 在很多时候会给我们的系统带来问题,这部分字符必须作特殊处理。 ?(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |