科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件序列化,反序列化时低序位非打印 ASCII 字符的问题

序列化,反序列化时低序位非打印 ASCII 字符的问题

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

低序位非打印 ASCII 字符 在很多时候会给我们的系统带来问题,这部分字符必须作特殊处理。

作者:ghj1976 来源:CSDN 2008年2月11日

关键字: Ascii 非打印 低序位

  • 评论
  • 分享微博
  • 分享邮件

最近碰到一个问题,我的一个把数据库中记录的信息暴露出来的Web Service调用时候出问题了。报下面的错误信息:

System.InvalidOperationException was unhandled
  Message="XML 文档(1, 823)中有错误。"
  Source="System.Xml"
    Message="“”(十六进制值 0x0E)是无效的字符。 行 1,位置 823。"
    Source="System.Xml"

当这个错误发生时,Web Service 服务器端不会有任何错误,而调用这个 Web Service 的客户端则会报上述错误。
是何原因导致的这个问题呢?
答案很简单,是WEB Service 暴露的XML文档中存在低序位非打印 ASCII 字符所致。
我们查看 Web Service 返回的XML 文档文档中,会有下面的XML文档节:其中的  就是低序位 ASCII 字符。对应的字符如后:

<Value>&#xE;在神奇天地裏誰叱咤風雨</Value>

会导致这些问题的 低序位非打印 ASCII 字符包含以下字符:
#x0 - #x8 (ASCII 0 - 8)
#xB - #xC (ASCII 11 - 12)
#xE - #x1F (ASCII 14 - 31)

下面就是一个简单演示这个问题的控制台程序,
为了简单起见,这里没有建立 WebService, 而是把一个类XML序列化存储到文件,然后再把这个文件反序列化读取出来:
其中的这个类的Value值中,放了一个低序位非打印 ASCII 字符。
执行这个控制台程序,就会报异常。“XML 文档(3, 12)中有错误。”

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 字符),则反序列化就会出错。

如果解决这个问题呢?

当然,最彻底的解决方法是修改反序列化的代码,让这些字符不会出错。但这个东西很多时候不归我们控制。这个方案不可行。

下一个方案就是剔除这些捣乱的字符。

我这里要给出的方案,是对这些字符序列化时作一次预处理,反序列化时,作一次反向处理。
这里为了演示的更有意义,我这里处理逻辑就是把 低序位非打印 ASCII 字符 转换成 &#x 编码的字符 ,和把&#x 编码的字符 转换成 低序位非打印 ASCII 字符。
这样就可以使用我这里提供的函数,实现更多的处理逻辑。这两个函数的代码如下:

 

        /// <summary>        /// 把一个字符串中的 低序位 ASCII 字符 替换成 &#x  字符         /// 转换  ASCII  0 - 8  -> &#x0 - &#x8        /// 转换  ASCII 11 - 12 -> &#xB - &#xC        /// 转换  ASCII 14 - 31 -> &#xE - &#x1F        /// </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 字符         /// 转换  &#x0 - &#x8  -> ASCII  0 - 8        /// 转换  &#xB - &#xC  -> ASCII 11 - 12        /// 转换  &#xE - &#x1F -> 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&#x50000"));            Console.WriteLine(GetLowOrderASCIICharacters("123456&#x5"));            Console.WriteLine(GetLowOrderASCIICharacters("&#x5"));            Console.WriteLine(GetLowOrderASCIICharacters("0123&#x1F;456789"));            Console.WriteLine(GetLowOrderASCIICharacters("\f"));
Console.WriteLine(GetLowOrderASCIICharacters("&#xE;=-1")); Console.WriteLine(GetLowOrderASCIICharacters("&#xF;")); Console.WriteLine(GetLowOrderASCIICharacters("&#x1F;")); string fileName = "d:\\1.txt"; MyClass c = new MyClass(); c.Value = string.Format("在神奇{0}天地裏誰叱咤風雨", 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(); }

小结

低序位非打印 ASCII 字符 在很多时候会给我们的系统带来问题,这部分字符必须作特殊处理。

查看本文来源
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章