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