15 Stimmen

XmlTextWriter schreibt fälschlicherweise Steuerzeichen

.NETs XmlTextWriter erzeugt ungültige xml-Dateien.

In XML sind einige Steuerzeichen erlaubt, wie z. B. 'horizontaler Tabulator' ( 	 ), aber andere sind es nicht, wie z. B. "vertikaler Tab" (  ). (Siehe spec .)

Ich habe eine Zeichenfolge, die ein UTF-8-Steuerzeichen enthält, das in XML nicht zulässig ist.
Obwohl XmlTextWriter das Zeichen umgeht, ist das resultierende XML natürlich immer noch ungültig.

Wie kann ich sicherstellen, dass XmlTextWriter nie eine illegale XML-Datei erzeugt?

Oder, falls dies nicht möglich ist, mit XmlTextWriter Wie kann ich die spezifischen Steuerzeichen, die in XML nicht zulässig sind, aus einer Zeichenfolge entfernen?

Beispiel-Code:

using (XmlTextWriter writer =
  new XmlTextWriter("test.xml", Encoding.UTF8))
{
  writer.WriteStartDocument();
  writer.WriteStartElement("Test");
  writer.WriteValue("hello \xb world");
  writer.WriteEndElement();
  writer.WriteEndDocument();
}

Salida:

<?xml version="1.0" encoding="utf-8"?><Test>hello &#xB; world</Test>

14voto

jasso Punkte 13386

Diese Dokumentation eines Verhaltens ist in der Dokumentation der WriteString-Methode aber es klingt so, als ob es für die ganze Klasse gilt.

Das Standardverhalten eines mit Create erstellten XmlWriters ist das Auslösen eines eine ArgumentException, wenn versucht wird, Zeichenwerte im Bereich 0x-0x1F (mit Ausnahme der Leerzeichen 0x9, 0xA und 0xD) zu schreiben. Diese ungültigen XML-Zeichen können durch Erstellen des XmlWriters geschrieben werden mit der Eigenschaft CheckCharacters auf false gesetzt werden. Dies wird dazu führen werden die Zeichen durch numerische Zeicheneinheiten ersetzt ( &#0; über &#0x1F ). Zusätzlich wird ein XmlTextWriter, der mit dem neuen Operator erstellte XmlTextWriter ersetzt die ungültigen Zeichen durch numerische Zeichen Einheiten ersetzt.

Es scheint also, dass Sie am Ende ungültige Zeichen schreiben, weil Sie die Klasse XmlTextWriter verwenden. Eine bessere Lösung für Sie wäre die Verwendung der XmlWriter-Klasse stattdessen.

6voto

rocco Punkte 304

Ich habe gerade diese Frage gefunden, als ich mit dem gleichen Problem zu kämpfen hatte, und ich habe es mit einer Regex gelöst:

return Regex.Replace(s, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", "");

Ich hoffe, es hilft jemandem als alternative Lösung.

1voto

Martijn Laarman Punkte 13386

Eingebaute .NET-Escaper wie z.B. SecurityElement.Escape auch nicht richtig entkommen/abstreifen.

  • Sie könnten CheckCharacters a false sowohl für den Schreiber als auch für den Leser, wenn Ihre Anwendung die einzige ist, die mit der Datei interagiert. Die resultierende XML-Datei wäre technisch gesehen immer noch ungültig Allerdings.

Siehe:

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = new UTF8Encoding(false);
xmlWriterSettings.CheckCharacters = false;
var sb = new StringBuilder();
var w = XmlWriter.Create(sb, xmlWriterSettings);
w.WriteStartDocument();
w.WriteStartElement("Test");
w.WriteString("hello \xb world");
w.WriteEndElement();
w.WriteEndDocument();
w.Close();
var xml = sb.ToString();
  • Wenn die Einstellung CheckCharacters a true (die es standardmäßig ist) ist ein bisschen zu streng, da es einfach eine Ausnahme werfen wird eine alternative Annäherung, die mehr Milde zu ungültigen XML-Zeichen ist, wäre, sie einfach zu entfernen:

Ein wenig Googeln ergab die Whitelist XmlTextEncoder wird jedoch auch entfernt DEL und andere im Bereich U+007F-U+0084, U+0086-U+009F, die nach der Gültige XML-Zeichen auf Wikipedia sind nur in bestimmten Kontexten gültig und werden im RFC als entmutigte, aber dennoch gültige Zeichen erwähnt.

public static class XmlTextExtentions
{
    private static readonly Dictionary<char, string> textEntities = new Dictionary<char, string> {
        { '&', "&amp;"}, { '<', "&lt;" }, { '>', "&gt;" }, 
        { '"', "&quot;" }, { '\'', "&apos;" }
    };
    public static string ToValidXmlString(this string str)
    {
        var stripped = str
            .Select((c,i) => new 
            { 
                c1 = c, 
                c2 = i + 1 < str.Length ? str[i+1]: default(char),
                v = XmlConvert.IsXmlChar(c),
                p = i + 1 < str.Length ? XmlConvert.IsXmlSurrogatePair(str[i + 1], c) : false,
                pp = i > 0 ? XmlConvert.IsXmlSurrogatePair(c, str[i - 1]) : false
            })
            .Aggregate("", (s, c) => {                  
                if (c.pp)
                    return s;
                if (textEntities.ContainsKey(c.c1))
                    s += textEntities[c.c1];
                else if (c.v)
                    s += c.c1.ToString();
                else if (c.p)
                    s += c.c1.ToString() + c.c2.ToString();
                return s;
            });
        return stripped;
    }
}

Dieser Test besteht alle XmlTextEncoder-Tests mit Ausnahme desjenigen, der erwartet, dass er DEL die XmlConvert.IsXmlChar , Wikipedia, und die Spezifikation kennzeichnet es als gültiges (wenn auch entmutigtes) Zeichen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X