您现在的位置是:网站首页>编程语言

C#实体类转xml时使用XmlWriter请注意xml的Encoding

编程语言阿文2020年4月05日340浏览

简介最近写一个接口需要用输出xml,回想以前做epub电子书时也用到xml,当时是手动操作xmldocument来创建node节点生成的xml,感觉输出xml没什么难度……

        最近写一个接口需要用输出xml,回想以前做epub电子书时也用到xml,当时是手动操作xmldocument来创建node节点生成的xml,感觉输出xml没什么难度, 于是就现看了一下C#实体类转xml,用到的方法是XmlSerializer,在.Net中命名空间是System.Xml.Serialization;

        由于是直接输出xml字符串,就用了以下方法:

            XmlSerializer xs = new XmlSerializer(typeof(obj));
            StringBuilder sb = new StringBuilder();
            using (XmlWriter writer = XmlWriter.Create(sb))
            {
                xs.Serialize(writer, obj);
            }
            return sb.ToString();

但是这样输出出来的xml字符串竟然encoding=""utf-16",what? 为什么不是和以前一样输出的xml长得不一样encoding=""utf-8",记得直接输出xml文件也是encoding=""utf-8",方法如下:

            XmlSerializer xs = new XmlSerializer(typeof(obj));
            using (StreamWriter sw = new StreamWriter("output.xml"))
            {
                xs.Serialize(sw, obj);
            }
            Console.WriteLine(File.ReadAllText("output.xml"))


仔细看了看发觉两者是不一样的地方是其中一个使用的是TextWriter,而另一个用的是XmlWriter。难道说XmlWriter的Encoding默认是utf-16不成?

查看了Serialize方法里面有一个重载可以用XmlWriterSettings来设置Encoding。具体改造方法如下:

            XmlSerializer xs = new XmlSerializer(typeof(obj));
            StringBuilder sb = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = Encoding.UTF8;
            settings.Indent = true;
            using (XmlWriter writer = XmlWriter.Create(sb, settings))
            {
                xs.Serialize(writer, obj);
            }
            return sb.ToString();

手动将XmlWriter设置为utf-8,这一下万事大吉了吧. 一运行程序看输出,很不幸,依旧输出encoding="utf-16"。见鬼了,难不成XmlWriter不支持utf-8?用脚趾头也可以想象这不大可能。

查MSDN终于在XmlWriterSettings.Encoding的文档里面发现下面一段话:

This property only applies to XmlWriter instances that output text content to a stream; otherwise, this setting is ignored.

看来XmlWriter只支持在Stream类的输出中设置Encoding。其实仔细想想也可以理解,只有stream类的输出,Encoding才可以用来进行编码,对于我们现在使用的StringBuilder来说,Xml的输出直接就变成了字符串了,没有任何编码过程,因此Encoding失效了。可以说当我们使用StringBuilder的时候,StringBuilder的Encodingoverwrite了XmlWriter的Encoding,而StringBuilder将会用StringWriter来包装,StringWriter.Encoding是Encoding.Unicode,也就是utf-16。因此,当我们使用StringBuilder作为XmlWriter的输出时,XmlWriter的Encoding就成了utf-16。而当我们把这个内存中的字符串,以utf-8写入文件的时候,虽然此时编码实际上为utf-8,但是并没有人负责把Xml声明的encoding="utf-16"改回"utf-8"了。错误就这样发生了。

问题明确了,解决起来并不复杂,我们用MemoryStream替换StringBuilder,如下:

            XmlSerializer xs = new XmlSerializer(typeof(obj));
            MemoryStream stream = new MemoryStream();
            XmlWriterSettings setting = new XmlWriterSettings();
            setting.Encoding = new UTF8Encoding(false);
            setting.Indent = true;
            using (XmlWriter writer = XmlWriter.Create(stream, settings))
            {
                xs.Serialize(writer, obj);
            }
            return Encoding.UTF8.GetString(stream.ToArray());

这回输出就正常了。这里需要注意的是Encoding那一行,这是设置Encoding不要输出BOM,否则生成的字符串前会有几个字节表示Byte Order

最后贴一个写好的封装方法:

        public static string XmlSerialize<T>(T obj)
        {
            System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
            MemoryStream stream = new MemoryStream();
            System.Xml.XmlWriterSettings setting = new System.Xml.XmlWriterSettings();
            setting.Encoding = new UTF8Encoding(false);
            setting.Indent = true;
            using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stream, setting))
            {
                xs.Serialize(writer, obj);
            }
            return Encoding.UTF8.GetString(stream.ToArray());
        }

因此,提醒诸位,如果使用非Stream类的输出,如StringBuilder/StringWriter,作为XmlWriter输出的话,请注意你的xml的Encoding


标签: .net XmlWriter Encoding

0

评论文明上网,理性发言0条评论