Сериализация XML с XmlWriter через StringBuilder - это utf-16, а через Stream - utf-8? - PullRequest
22 голосов
/ 12 мая 2011

Я был удивлен, когда столкнулся с ним, и написал консольное приложение, чтобы проверить его и убедиться, что я больше ничего не делаю.

Может кто-нибудь объяснить это?

Вот код:

using System;    
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            var o = new SomeObject { Field1 = "string value", Field2 = 8 };

            Console.WriteLine("ObjectToXmlViaStringBuilder");
            Console.Write(ObjectToXmlViaStringBuilder(o));
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("ObjectToXmlViaStream");
            Console.Write(StreamToString(ObjectToXmlViaStream(o)));
            Console.ReadKey();
        }

        public static string ObjectToXmlViaStringBuilder(SomeObject someObject)
        {
            var output = new StringBuilder();
            var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true };

            using (var xmlWriter = XmlWriter.Create(output, settings))
            {
                var serializer = new XmlSerializer(typeof(SomeObject));
                var namespaces = new XmlSerializerNamespaces();

                xmlWriter.WriteStartDocument();
                xmlWriter.WriteDocType("Field1", null, "someObject.dtd", null);
                namespaces.Add(string.Empty, string.Empty);
                serializer.Serialize(xmlWriter, someObject, namespaces);
            }

            return output.ToString();
        }

        private static string StreamToString(Stream stream)
        {
            var reader = new StreamReader(stream);
            return reader.ReadToEnd();
        }

        public static Stream ObjectToXmlViaStream(SomeObject someObject)
        {
            var output = new MemoryStream();
            var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true };

            using (var xmlWriter = XmlWriter.Create(output, settings))
            {
                var serializer = new XmlSerializer(typeof(SomeObject));
                var namespaces = new XmlSerializerNamespaces();

                xmlWriter.WriteStartDocument();
                xmlWriter.WriteDocType("Field1", null, "someObject.dtd", null);
                namespaces.Add(string.Empty, string.Empty);
                serializer.Serialize(xmlWriter, someObject, namespaces);
            }

            output.Seek(0L, SeekOrigin.Begin);

            return output;
        }

        public class SomeObject
        {
            public string Field1 { get; set; }
            public int Field2 { get; set; }
        }
    }
}

Это результат:

ObjectToXmlViaStringBuilder

<?xml version="1.0" encoding="utf-16"?>
<!DOCTYPE Field1 SYSTEM "someObject.dtd">
<SomeObject>
<Field1>string value</Field1>
<Field2>8</Field2>
</SomeObject>

ObjectToXmlViaStream

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Field1 SYSTEM "someObject.dtd">
<SomeObject>
<Field1>string value</Field1>
<Field2>8</Field2>
</SomeObject>

Ответы [ 3 ]

30 голосов
/ 12 мая 2011

Когда вы создаете XmlWriter вокруг TextWriter, XmlWriter всегда использует кодировку базового TextWriter. Кодировка StringWriter всегда UTF-16, так как строки .NET внутренне кодируются.

Когда вы создаете XmlWriter вокруг Stream, для Stream не определена кодировка, поэтому она использует кодировку, указанную в XmlWriterSettings.

4 голосов
/ 30 марта 2012

Самым элегантным решением для меня является запись в поток памяти, а затем использование кодирования для кодирования потока в любую необходимую кодировку. вот так

        using (MemoryStream memS = new MemoryStream())
        {
            //set up the xml settings
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.OmitXmlDeclaration = OmitXmlHeader;

            using (XmlWriter writer = XmlTextWriter.Create(memS, settings))
            {
                //write the XML to a stream
                xmlSerializer.Serialize(writer, objectToSerialize);
                writer.Close();
            }
            //encode the memory stream to xml
            retString.AppendFormat("{0}", encoding.GetString(memS.ToArray()));
            memS.Close();
        }

где кодирование происходит в .... encoding.GetString (memS.ToArray ()) ...

2 голосов
/ 12 мая 2011

Где возможно, XmlWriter использует кодировку базового потока. Если бы он записал данные UTF-8 в поток, который, как он знал, был UTF-16, вы получите беспорядок. Запись данных UTF-16 в поток UTF-8 также вызывает проблемы, особенно для сред, в которых используются строки с нулевым символом в конце (например, C / C ++).

StringBuilder / StringWriter представляет поток UTF-16 для XmlWriter, поэтому XmlWriter игнорирует запрошенный параметр и использует его.

На практике я обычно не генерирую заголовок, таким образом, я могу использовать StringBuilder внизу и сохранить несколько строк кода, мешающих переключать кодировки.

...