Как поместить атрибут кодирования в xml, отличный от utf-16 с помощью XmlWriter? - PullRequest
38 голосов
/ 09 января 2009

У меня есть функция создания некоторого XmlDocument:

public string CreateOutputXmlString(ICollection<Field> fields)
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.Encoding = Encoding.GetEncoding("windows-1250");

    StringBuilder builder = new StringBuilder();
    XmlWriter writer = XmlWriter.Create(builder, settings);

    writer.WriteStartDocument();
    writer.WriteStartElement("data");
    foreach (Field field in fields)
    {
        writer.WriteStartElement("item");
        writer.WriteAttributeString("name", field.Id);
        writer.WriteAttributeString("value", field.Value);
        writer.WriteEndElement();
    }
    writer.WriteEndElement();
    writer.Flush();
    writer.Close();

    return builder.ToString();
}

Я установил кодировку, но после создания XmlWriter у нее есть кодировка utf-16. Я знаю, что это потому, что строки (и, я полагаю, StringBuilder) закодированы в utf-16, и вы не можете это изменить.
Итак, как я могу легко создать этот XML с атрибутом кодировки, установленным в «windows-1250»? он даже не должен кодироваться в этой кодировке, он просто должен иметь указанный атрибут.

edit: он должен быть в .Net 2.0, чтобы никакие новые элементы каркаса не могли быть использованы.

Ответы [ 5 ]

72 голосов
/ 09 января 2009

Вам необходимо использовать StringWriter с соответствующей кодировкой. К сожалению, StringWriter не позволяет вам указывать кодировку напрямую, поэтому вам нужен такой класс:

public sealed class StringWriterWithEncoding : StringWriter
{
    private readonly Encoding encoding;

    public StringWriterWithEncoding (Encoding encoding)
    {
        this.encoding = encoding;
    }

    public override Encoding Encoding
    {
        get { return encoding; }
    }
}

( Этот вопрос похож, но не совсем повторяется.)

РЕДАКТИРОВАТЬ: Чтобы ответить на комментарий: передайте StringWriterWithEncoding XmlWriter.Create вместо StringBuilder, затем вызовите ToString () для него в конце.

5 голосов
/ 25 июля 2013
MemoryStream memoryStream = new MemoryStream();
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = Encoding.UTF8;

XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings);
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("root", "http://www.timvw.be/ns");
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
xmlWriter.Flush();
xmlWriter.Close();

string xmlString = Encoding.UTF8.GetString(memoryStream.ToArray());

Отсюда

5 голосов
/ 14 февраля 2013

Просто несколько дополнительных объяснений, почему это так.

Строки - это последовательности символов, а не байты. Строки сами по себе не являются «кодированными», поскольку они используют символы, которые хранятся в виде кодовых точек Unicode. Кодирование НЕ ОСУЩЕСТВЛЯЕТ СМЫСЛ на строковом уровне.

Кодировка - это отображение последовательности кодовых точек (символов) в последовательность байтов (для хранения в системах, основанных на байтах, таких как файловые системы или память). Инфраструктура не позволяет указывать кодировки, если нет веских причин, например, чтобы 16-разрядные кодовые точки помещались в хранилище на основе байтов.

Таким образом, когда вы пытаетесь записать свой XML в StringBuilder, вы фактически строите последовательность символов XML и записываете их как последовательность символов, поэтому кодирование не выполняется. Следовательно, поле Кодировка отсутствует.

Если вы хотите использовать кодировку, XmlWriter должен выполнить запись в поток.

О решении, которое вы нашли с MemoryStream, без обид, но оно просто развевается вокруг рук и движется горячим воздухом. Вы кодируете свои кодовые точки с помощью «windows-1252», а затем снова анализируете их до кодовых точек. Единственное изменение, которое может произойти, - это то, что символы, не определенные в windows-1252, преобразуются в '?' персонаж в процессе.

Для меня правильным решением может быть следующее. В зависимости от того, для чего используется ваша функция, вы можете передать Stream в качестве параметра вашей функции, чтобы вызывающий абонент решал, следует ли записывать его в память или в файл. Так было бы написано так:


        public static void WriteFieldsAsXmlDocument(ICollection fields, Stream outStream)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.Encoding = Encoding.GetEncoding("windows-1250");

            using(XmlWriter writer = XmlWriter.Create(outStream, settings)) {
                writer.WriteStartDocument();
                writer.WriteStartElement("data");
                foreach (Field field in fields)
                {
                    writer.WriteStartElement("item");
                    writer.WriteAttributeString("name", field.Id);
                    writer.WriteAttributeString("value", field.Value);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
        }
3 голосов
/ 09 января 2009

Я действительно решил проблему с MemoryStream:

public static string CreateOutputXmlString(ICollection<Field> fields)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.Encoding = Encoding.GetEncoding("windows-1250");

            MemoryStream memStream = new MemoryStream();
            XmlWriter writer = XmlWriter.Create(memStream, settings);

            writer.WriteStartDocument();
            writer.WriteStartElement("data");
            foreach (Field field in fields)
            {
                writer.WriteStartElement("item");
                writer.WriteAttributeString("name", field.Id);
                writer.WriteAttributeString("value", field.Value);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
            writer.Flush();
            writer.Close();

            writer.Flush();
            writer.Close();

            string xml = Encoding.GetEncoding("windows-1250").GetString(memStream.ToArray());

            memStream.Close();
            memStream.Dispose();

            return xml;
        }
0 голосов
/ 11 декабря 2016

Я решил свою проблему, выведя строку в переменную и заменив любые ссылки на utf-16 на utf-8 (моему приложению требовалась кодировка UTF8). Поскольку вы используете функцию, вы можете сделать что-то подобное. В основном я использую VB.net, но думаю, что C # будет выглядеть примерно так.

return builder.ToString().Replace("utf-16", "utf-8");
...