zoukankan      html  css  js  c++  java
  • 由一个Xml序列化操作看mscorlib.dll 2.0、4.0 String的Trim方法实现

    有一段Xml序列化的代码,基于2.0 Runtime传递到Server转换正常。当客户端在4.0 Runtime下调用,Server返回格式错误。序列化代码如下:

    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
    
    using (MemoryStream memoryStream = new MemoryStream())
    {
        XmlTextWriter xmlWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
        xmlSerializer.Serialize(xmlWriter, graph, ns);
    
        return Encoding.UTF8.GetString(memoryStream.ToArray()).Trim();
    }

    用Microsoft Network Monitor监视4.0 Runtime下发送的数据内容,转换后的Xml头部多出了3个字节:EF(239) BB(187) BF(191),它是UTF-8的preamble(encoding bites),字符值为65279。.NET Framework 2.0 String的Trim函数内部会去除一组预定义的空白字符集,其中就包括了65279。反编译代码如下(最后一个字符'\ufeff'是62579的16进制表示):

    public string Trim()
    {
        return this.TrimHelper(string.WhitespaceChars, 2);
    }
    internal static readonly char[] WhitespaceChars = new char[25]
    {
        '\t',
        '\n',
        '\v',
        '\f',
        '\r',
        ' ',
        '\u0085',
        '\u00a0',
        '\u1680',
        '\u2000',
        '\u2001',
        '\u2002',
        '\u2003',
        '\u2004',
        '\u2005',
        '\u2006',
        '\u2007',
        '\u2008',
        '\u2009',
        '\u200a',
        '\u200b',
        '\u2028',
        '\u2029',
        '\u3000',
        '\ufeff'
    };

    .NET Framework 4.0 String的Trim函数,反编译代码如下:

    public string Trim()
    {
        return this.TrimHelper(2);
    }

    4.0版本String的Trim函数内部不再去除一组预定义的空白字符集,直接导致了之前Xml序列化无法去除UTF-8编码后的preamble。解决方法就是根据不同字符编码尝试去除转换后的preamble。代码如下:

    public static string TrimPreamble(this string value, Encoding encoding)
    {
        if (String.IsNullOrEmpty(value))
        {
            return value;
        }
    
        var encodingString = encoding.GetString(encoding.GetPreamble());
    
        if (value.Length <= encodingString.Length)
        {
            return value;
        }
    
        for (var i = 0; i < encodingString.Length; ++i)
        {
            if (value[i] != encodingString[i])
            {
                return value;
            }
        }
    
        return value.Remove(0, encodingString.Length);
    }

    在我的实现版本并没有使用StartWith或IndexOf函数是有原因的,比如上面的函数可以简写成:

    public static string TrimPreamble(this string value, Encoding encoding)
    {
        if (String.IsNullOrEmpty(value))
        {
            return value;
        }
    
        var encodingString = encoding.GetString(encoding.GetPreamble());
    
        if (value.StartsWith(encodingString))
        {
            value = value.Remove(0, encodingString.Length);
        }
    
        return value;
    }

    当客户端基于.NET Framework 2.0,发送字符串到基于.NET Framework 4.0运行的Server,preamble的偏移在实际字符的-1位。StartWith、IndexOf函数的匹配并不是针对String实际可见的char array逐一匹配,由.NET平台决定字符串在内存的存储方式,它们会返回字符已存在,但并不存在于可见的char array中。具体原因可以查看System.Globalization.CompareInfo.InternalFindNLSStringEx函数。

    不使用XmlTextWriter可以避免encoding bites,比如:StringWriter。其缺点是无法指定Encoding,但可以继承StringWriter并重新定义它的构造函数。

    public sealed class EncodingStringWriter : StringWriter
    {
        private Encoding _encoding;
    
        public EncodingStringWriter(StringBuilder sb, Encoding encoding)
            : base(sb)
        {
            _encoding = encoding;
        }
    
        public override Encoding Encoding
        {
            get
            {
                return _encoding;
            }
        }
    }
  • 相关阅读:
    [复变函数]第07堂课 2.2 初等解析函数
    [家里蹲大学数学杂志]第237期Euler公式的美
    [家里蹲大学数学杂志]第287期复变函数讲义
    [家里蹲大学数学杂志]第253期实变函数讲义
    模仿王者荣耀的实时阴影
    Android 识别身份证号码(图片识别)
    基于swiper的移动端H5页面,丰富的动画效果
    基于skitter的轮播图炫酷效果,幻灯片的体验
    基于canvas的原生JS时钟效果
    .net core 实现简单爬虫—抓取博客园的博文列表
  • 原文地址:https://www.cnblogs.com/junchu25/p/2761018.html
Copyright © 2011-2022 走看看