如何在使用XmlWriter时为XML添加编码属性而不是utf-16?

39

我有一个函数创建了一些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,而且你无法更改它。
那么,如何轻松地创建具有编码属性设置为“windows-1250”的xml?它甚至不必以这种编码进行编码,只需要具有指定的属性即可。

编辑:必须在 .Net 2.0 中实现,因此不能使用任何新的框架元素。


我知道这可能不是正确的方法,但当我想要返回我的XML字符串时,我使用了blahblah.Replace("utf-16","utf-8"),并且它对我起作用了 :D - Arya Aghaei
5个回答

82

您需要使用适当编码的 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()方法。


好的,这是一个字符串编写器,我之前在另一个主题中看到过这篇帖子,但我不知道我可以用它做什么。 - agnieszka
请问您能否解释一下我该如何使用它? - agnieszka
请看我在结尾处的编辑。只需将您的 StringBuilder 更改为 StringWriterWithEncoding(传入所需的编码),就完成了。 - Jon Skeet
这个解决方案非常好--谢谢。用包装类有点像是hack,但你能做什么呢... - nickb
谢谢。我使用了你的帖子。如果有帮助的话,我写了一个Xml类来为您完成此操作。它还可以执行其他一些操作,例如线性化和美化Xml。它还将UTF-8大写。http://www.rhyous.com/2015/04/07/an-xml-class-to-linearize-xml-make-pretty-xml-and-encoding-in-utf-8-or-utf-16/ - Rhyous
1
还要注意,如果您在StringWriter专业化中让Encoding返回null,那么XmlWriter将仅写入XML声明,即<?xml version="1.0"?>,即根本没有任何编码信息。这是一个完全有效的XML声明。如果我们对编码不确定,这是非常有用的。 - Jeppe Stig Nielsen

5
只是一些额外的解释,为什么会这样。
字符串是字符序列,而不是字节。字符串本身并没有“编码”,因为它们使用的是Unicode代码点存储的字符。在字符串级别上进行编码是没有意义的。
编码是从一系列代码点(字符)到一系列字节(用于存储在基于字节的系统上,如文件系统或内存)的映射。除非有充分的理由(例如使16位代码点适合基于字节的存储),否则框架不会让你指定编码。
因此,当您尝试将XML写入StringBuilder时,实际上是构建一个XML字符序列,并将它们作为字符序列写入,因此不会执行任何编码。因此,没有编码字段。
如果要使用编码,XmlWriter必须写入Stream。
关于您找到的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();
            }
        }

5
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());

From here


3

我实际上使用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;
        }

1
如果你要使用MemoryStream,你应该至少使用与之前相同的编码进行解码(即Windows-1250,而不是ASCII)。个人而言,我更喜欢我的版本 :) - Jon Skeet

0

我通过将字符串输出到一个变量,然后用 utf-8 替换任何对 utf-16 的引用(我的应用程序需要 UTF8 编码)来解决了我的问题。由于你正在使用一个函数,你可以做类似的事情。我主要使用 VB.net,但我认为 C# 的代码应该是这样的。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接