您现在的位置是:网站首页>编程语言
C#实体类转xml时使用XmlWriter请注意xml的Encoding
【编程语言】阿文2020年4月05日726浏览
简介最近写一个接口需要用输出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。
上一篇: 消息队列的使用场景大概是怎样的?
下一篇: C#去除字符串中的幽灵字符
评论文明上网,理性发言0条评论