Сериализация объекта в формате UTF-8 XML in.NET
правильное удаление объекта удалено для краткости, но я в шоке, если это самый простой способ кодировать объект как 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();
4 ответа:
ваш код не получает UTF-8 в память, когда вы снова читаете его в строку, поэтому его больше нет в UTF-8, но обратно в UTF-16 (хотя в идеале лучше всего рассматривать строки на более высоком уровне, чем любая кодировка, за исключением случаев, когда это необходимо сделать).
чтобы получить фактические октеты 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(); }
который очень такая же сложность, но показывает, что на каждом этапе есть разумный выбор сделать что-то еще, самым насущным из которых является сериализация в другое место, чем в память, например, в файл, поток TCP/IP, базу данных и т. д. В общем, это не так многословно.
нет, вы можете использовать
StringWriter
чтобы избавиться от промежуточногоMemoryStream
. Однако, чтобы заставить его в XML вам нужно использоватьStringWriter
, который переопределяетEncoding
свойства: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 является наиболее часто требуемой" пользовательской " кодировкой для aStringWriter
:)теперь, как говорит Джон Ханна, это все равно будет UTF-16 внутренне, но предположительно вы собираетесь передать его в какой-то другой момент, чтобы преобразовать его в двоичные данные... в это point вы можете использовать приведенную выше строку, преобразовать ее в UTF-8 байт, и все будет хорошо - потому что XML-декларация будет указывать "utf-8" в качестве кодировки.
EDIT: короткий, но полный пример, чтобы показать это рабочий:
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", которую мы хотели, я считаю.
очень хороший ответ с использованием наследования, просто не забудьте переопределить инициализатор
public class Utf8StringWriter : StringWriter { public Utf8StringWriter(StringBuilder sb) : base (sb) { } public override Encoding Encoding { get { return Encoding.UTF8; } } }
Я нашел это сообщение в блоге, которое очень хорошо объясняет проблему и определяет несколько различных решений:
(недоступная ссылка-удален)
я согласился с идеей, что лучший способ сделать это-полностью опустить объявление XML, когда оно находится в памяти. Это на самом деле и UTF-16 в этот момент в любом случае, но объявление XML не кажется значимым, пока оно не будет записано в файл с определенной кодировкой; и даже тогда объявление не требуется. Оно по крайней мере, не нарушается десериализация.
Как упоминает @Jon Hanna, это можно сделать с помощью XmlWriter, созданного следующим образом:
XmlWriter writer = XmlWriter.Create (output, new XmlWriterSettings() { OmitXmlDeclaration = true });