在.NET中将对象序列化为UTF-8 XML

正确的对象处置删除简洁,但我很震惊,如果这是最简单的方法编码对象为UTF-8在内存中。 那里有一个更简单的方法吗?

var serializer = new XmlSerializer(typeof(SomeSerializableObject)); var memoryStream = new MemoryStream(); var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8); serializer.Serialize(streamWriter, entry); memoryStream.Seek(0, SeekOrigin.Begin); var streamReader = new StreamReader(memoryStream, System.Text.Encoding.UTF8); var utf8EncodedXml = streamReader.ReadToEnd(); 

你的代码不会再把UTF-8读回到内存中,所以它不再使用UTF-8,而是使用UTF-16(虽然理想情况下最好考虑比更高级别的string任何编码,除非被迫这样做)。

要获得实际的UTF-8八位字节,您可以使用:

 var serializer = new XmlSerializer(typeof(SomeSerializableObject)); var memoryStream = new MemoryStream(); var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8); serializer.Serialize(streamWriter, entry); byte[] utf8EncodedXml = memoryStream.ToArray(); 

我遗漏了你已经离开的相同处置。 我稍微赞成以下(正常处置剩下):

 var serializer = new XmlSerializer(typeof(SomeSerializableObject)); using(var memStm = new MemoryStream()) using(var xw = XmlWriter.Create(memStm)) { serializer.Serialize(xw, entry); var utf8 = memStm.ToArray(); } 

这种复杂性的数量是相同的,但是确实表明,在每一个阶段都有一个合理的select来做别的事情,其中​​最紧迫的就是将序列化到除了内存之外的某个地方,比如到一个文件,TCP / IPstream,数据库等。总而言之,这并不是那么冗长。

不,您可以使用StringWriter来摆脱中间的MemoryStream 。 但是,要将其强制为XML,您需要使用重写Encoding属性的StringWriter

 public class Utf8StringWriter : StringWriter { public override Encoding Encoding => Encoding.UTF8; } 

或者如果您还没有使用C#6:

 public class Utf8StringWriter : StringWriter { public override Encoding Encoding { get { return Encoding.UTF8; } } } 

然后:

 var serializer = new XmlSerializer(typeof(SomeSerializableObject)); string utf8; using (StringWriter writer = new Utf8StringWriter()) { serializer.Serialize(writer, entry); utf8 = writer.ToString(); } 

显然你可以使Utf8StringWriter成为一个更通用的类,它接受任何编码的构造函数 – 但根据我的经验,UTF-8是StringWriter最常用的“自定义”编码:)

现在Jon Hanna说,这个在内部仍然是UTF-16,但是大概你会在某个时候把它传递给别的东西,把它转换成二进制数据…在一点上,你可以使用上面的string,将其转换为UTF-8字节,并且一切都会好 – 因为XML声明将指定“utf-8”作为编码。

编辑:一个简短但完整的例子来显示这个工作:

 using System; using System.Text; using System.IO; using System.Xml.Serialization; public class Test { public int X { get; set; } static void Main() { Test t = new Test(); var serializer = new XmlSerializer(typeof(Test)); string utf8; using (StringWriter writer = new Utf8StringWriter()) { serializer.Serialize(writer, t); utf8 = writer.ToString(); } Console.WriteLine(utf8); } public class Utf8StringWriter : StringWriter { public override Encoding Encoding => Encoding.UTF8; } } 

结果:

 <?xml version="1.0" encoding="utf-8"?> <Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <X>0</X> </Test> 

请注意,我相信我们想要的是“utf-8”的声明编码。

很好的回答使用inheritance,只要记住重写初始值设定项

 public class Utf8StringWriter : StringWriter { public Utf8StringWriter(StringBuilder sb) : base (sb) { } public override Encoding Encoding { get { return Encoding.UTF8; } } } 

我发现这个博客文章很好地解释了这个问题,并定义了几个不同的解决scheme:

提示:使用StringBuilder强制UTF8或XmlWriter的其他编码

我已经解决了这个想法,最好的办法是在内存中完全省略XML声明。 实际上,在这一点上,UTF-16实际上 UTF-16,但是XML声明在被写入具有特定编码的文件之前似乎没有意义。 即使这样,声明也不是必需的。 至less,它似乎并没有打破反序列化。

正如@Jon Hanna所提到的,这可以用像这样创build的XmlWriter完成:

 XmlWriter writer = XmlWriter.Create (output, new XmlWriterSettings() { OmitXmlDeclaration = true });