it-swarm.com.de

Wie lege ich mit XmlWriter ein Codierungsattribut auf eine andere XML als utf-16?

Ich habe eine Funktion, die XmlDocument erstellt:

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

Ich habe eine Kodierung festgelegt, aber nachdem ich XmlWriter erstellt habe, hat es die Kodierung utf-16. Ich weiß, dass es daran liegt, dass Strings (und vermutlich StringBuilder) in utf-16 codiert sind und Sie es nicht ändern können.
Wie kann ich diese XML-Datei mit dem Codierungsattribut "windows-1250" erstellen? es muss nicht einmal in dieser Kodierung kodiert werden, es muss nur das angegebene Attribut haben.

edit: Es muss in .Net 2.0 sein, damit keine neuen Framework-Elemente verwendet werden können.

36
agnieszka

Sie müssen einen StringWriter mit der entsprechenden Kodierung verwenden. Leider können Sie mit StringWriter die Kodierung nicht direkt angeben, daher benötigen Sie eine Klasse wie diese:

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

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

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

( Diese Frage ist ähnlich, aber nicht ganz ein Duplikat.)

BEARBEITEN: Um den Kommentar zu beantworten: Übergeben Sie StringWriterWithEncoding an XmlWriter.Create anstelle des StringBuilder und rufen Sie am Ende ToString () auf.

71
Jon Skeet

Nur ein paar zusätzliche Erklärungen, warum das so ist.

Zeichenfolgen sind Folgen von Zeichen, nicht Bytes. Zeichenfolgen an sich werden nicht "codiert", da sie Zeichen verwenden, die als Unicode-Codepunkte gespeichert sind. Die Kodierung macht keinen Sinn auf String-Ebene.

Eine Kodierung ist eine Abbildung von einer Folge von Codepunkten (Zeichen) auf eine Folge von Bytes (zur Speicherung auf bytebasierten Systemen wie Dateisystemen oder Arbeitsspeicher). Das Framework lässt Sie keine Kodierungen angeben, es sei denn, es besteht ein zwingender Grund dafür, dass 16-Bit-Codepunkte in byte-basierten Speicher passen sollen.

Wenn Sie also versuchen, Ihr XML in einen StringBuilder zu schreiben, erstellen Sie tatsächlich eine XML-Folge von Zeichen und schreiben diese als Folge von Zeichen, sodass keine Kodierung erfolgt. Daher kein Encoding-Feld.

Wenn Sie eine Codierung verwenden möchten, muss der XmlWriter in einen Stream schreiben.

Über die Lösung, die Sie mit dem MemoryStream gefunden haben, ist keine Beleidigung beabsichtigt, sondern es flattert nur um die Arme und bewegt heiße Luft. Sie codieren Ihre Codepunkte mit 'windows-1252' und analysieren sie dann wieder in Codepunkte. Die einzige Änderung, die auftreten kann, besteht darin, dass Zeichen, die nicht in Windows-1252 definiert sind, in ein "?" Zeichen im Prozess.

Für mich könnte die richtige Lösung die folgende sein. Abhängig davon, wozu Ihre Funktion verwendet wird, können Sie Ihrer Funktion einen Stream als Parameter übergeben, sodass der Anrufer entscheidet, ob er in den Speicher oder in eine Datei geschrieben werden soll. Also wäre es so geschrieben:


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

Von hier

5
EddiG

Ich habe das Problem mit MemoryStream tatsächlich gelöst:

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;
        }
3
agnieszka

Ich löste meine, indem ich den String in eine Variable ausgab und dann alle Verweise auf utf-16 durch utf-8 ersetzte (meine App benötigte UTF8-Codierung). Da Sie eine Funktion verwenden, können Sie etwas Ähnliches tun. Ich benutze hauptsächlich VB.net, aber ich denke, dass C # so aussehen würde.

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