it-swarm.com.de

Beste Möglichkeit, Textdaten für XML zu kodieren

Ich suchte nach einer generischen Methode in .Net, um eine Zeichenfolge für die Verwendung in einem XML-Element oder -Attribut zu codieren, und war überrascht, als ich nicht sofort eine fand. Könnte mir also, bevor ich zu weit gehe, die eingebaute Funktion fehlen?

Angenommen, es existiert für einen Moment wirklich nicht, dann stelle ich meine eigene generische EncodeForXml(string data) -Methode zusammen und überlege, wie ich das am besten bewerkstelligen kann.

Die Daten, die ich verwende, die dazu veranlasst haben, dass diese ganze Sache schlechte Zeichen wie <, "usw. enthält. Sie können gelegentlich auch die ordnungsgemäß ausgeblendeten Entitäten amp ;, lt; und quot; enthalten, was bedeutet, dass nur eine CDATA verwendet wird section ist vielleicht nicht die beste Idee. Das scheint irgendwie klunkig zu sein. Ich möchte lieber einen Nice-String-Wert haben, der direkt in der XML verwendet werden kann.

Ich habe in der Vergangenheit einen regulären Ausdruck verwendet, um nur schlechte kaufmännische Und-Zeichen abzufangen, und ich denke darüber nach, ihn zu verwenden, um sie in diesem Fall sowie im ersten Schritt abzufangen und dann andere Zeichen einfach zu ersetzen.

Könnte dies weiter optimiert werden, ohne es zu komplex zu machen, und fehlt mir etwas? :

Function EncodeForXml(ByVal data As String) As String
    Static badAmpersand As new Regex("&(?![a-zA-Z]{2,6};|#[0-9]{2,4};)")

    data = badAmpersand.Replace(data, "&amp;")

    return data.Replace("<", "&lt;").Replace("""", "&quot;").Replace(">", "gt;")
End Function

Tut mir leid, dass Sie nur C # verwenden. Es ist mir egal, welche Sprache ich verwende, aber ich wollte den Regex statisch machen, und das können Sie in C # nicht tun, ohne es zu deklarieren außerhalb der Methode, so wird dies VB.Net sein

Schließlich arbeiten wir immer noch mit .NET 2.0, aber wenn jemand das Endprodukt in eine Erweiterungsmethode für die Zeichenfolgenklasse umwandeln könnte, wäre das auch ziemlich cool.

pdate Die ersten Antworten deuten darauf hin, dass .Net in der Tat eingebaute Möglichkeiten hat, dies zu tun. Aber jetzt, wo ich angefangen habe, möchte ich meine EncodeForXml () -Methode zum Spaß beenden, also bin ich immer noch auf der Suche nach Verbesserungsvorschlägen. Insbesondere: Eine vollständigere Liste von Zeichen, die als Entitäten codiert werden sollten (möglicherweise in einer Liste/Map gespeichert), und etwas, das eine bessere Leistung erzielt als ein .Replace () für unveränderliche Zeichenfolgen in Serie.

67
Joel Coehoorn

System.XML übernimmt die Codierung für Sie, sodass Sie eine solche Methode nicht benötigen.

3
MusiGenesis

Je nachdem, wie viel Sie über die Eingabe wissen, müssen Sie möglicherweise berücksichtigen, dass nicht alle Unicode-Zeichen sind gültige XML-Zeichen .

Beide Server.HtmlEncode und System.Security.SecurityElement.Escape scheinen unzulässige XML-Zeichen zu ignorieren, während System.XML.XmlWriter.WriteString löst eine ArgumentException aus, wenn er auf ungültige Zeichen stößt (es sei denn, Sie deaktivieren diese Prüfung, in welchem ​​Fall er sie ignoriert). Eine Übersicht der Bibliotheksfunktionen ist verfügbar hier .

Edit 2011/8/14: Da zumindest einige Leute diese Antwort in den letzten Jahren konsultiert haben, habe ich beschlossen, den Originalcode komplett neu zu schreiben , die zahlreiche Probleme hatte, einschließlich schrecklich falsches Behandeln von UTF-16 .

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

/// <summary>
/// Encodes data so that it can be safely embedded as text in XML documents.
/// </summary>
public class XmlTextEncoder : TextReader {
    public static string Encode(string s) {
        using (var stream = new StringReader(s))
        using (var encoder = new XmlTextEncoder(stream)) {
            return encoder.ReadToEnd();
        }
    }

    /// <param name="source">The data to be encoded in UTF-16 format.</param>
    /// <param name="filterIllegalChars">It is illegal to encode certain
    /// characters in XML. If true, silently omit these characters from the
    /// output; if false, throw an error when encountered.</param>
    public XmlTextEncoder(TextReader source, bool filterIllegalChars=true) {
        _source = source;
        _filterIllegalChars = filterIllegalChars;
    }

    readonly Queue<char> _buf = new Queue<char>();
    readonly bool _filterIllegalChars;
    readonly TextReader _source;

    public override int Peek() {
        PopulateBuffer();
        if (_buf.Count == 0) return -1;
        return _buf.Peek();
    }

    public override int Read() {
        PopulateBuffer();
        if (_buf.Count == 0) return -1;
        return _buf.Dequeue();
    }

    void PopulateBuffer() {
        const int endSentinel = -1;
        while (_buf.Count == 0 && _source.Peek() != endSentinel) {
            // Strings in .NET are assumed to be UTF-16 encoded [1].
            var c = (char) _source.Read();
            if (Entities.ContainsKey(c)) {
                // Encode all entities defined in the XML spec [2].
                foreach (var i in Entities[c]) _buf.Enqueue(i);
            } else if (!(0x0 <= c && c <= 0x8) &&
                       !new[] { 0xB, 0xC }.Contains(c) &&
                       !(0xE <= c && c <= 0x1F) &&
                       !(0x7F <= c && c <= 0x84) &&
                       !(0x86 <= c && c <= 0x9F) &&
                       !(0xD800 <= c && c <= 0xDFFF) &&
                       !new[] { 0xFFFE, 0xFFFF }.Contains(c)) {
                // Allow if the Unicode codepoint is legal in XML [3].
                _buf.Enqueue(c);
            } else if (char.IsHighSurrogate(c) &&
                       _source.Peek() != endSentinel &&
                       char.IsLowSurrogate((char) _source.Peek())) {
                // Allow well-formed surrogate pairs [1].
                _buf.Enqueue(c);
                _buf.Enqueue((char) _source.Read());
            } else if (!_filterIllegalChars) {
                // Note that we cannot encode illegal characters as entity
                // references due to the "Legal Character" constraint of
                // XML [4]. Nor are they allowed in CDATA sections [5].
                throw new ArgumentException(
                    String.Format("Illegal character: '{0:X}'", (int) c));
            }
        }
    }

    static readonly Dictionary<char,string> Entities =
        new Dictionary<char,string> {
            { '"', "&quot;" }, { '&', "&amp;"}, { '\'', "&apos;" },
            { '<', "&lt;" }, { '>', "&gt;" },
        };

    // References:
    // [1] http://en.wikipedia.org/wiki/UTF-16/UCS-2
    // [2] http://www.w3.org/TR/xml11/#sec-predefined-ent
    // [3] http://www.w3.org/TR/xml11/#charsets
    // [4] http://www.w3.org/TR/xml11/#sec-references
    // [5] http://www.w3.org/TR/xml11/#sec-cdata-sect
}

Unit-Tests und vollständiger Code finden Sie hier .

77
Michael Kropat

SecurityElement.Escape

dokumentiert hier

31
workmad3

In der Vergangenheit habe ich HttpUtility.HtmlEncode verwendet, um Text für XML zu kodieren. Es führt wirklich die gleiche Aufgabe aus. Ich habe noch keine Probleme damit, aber das heißt nicht, dass ich es in Zukunft nicht tun werde. Wie der Name schon sagt, wurde es für HTML erstellt, nicht für XML.

Sie haben es wahrscheinlich schon gelesen, aber hier ist ein Artikel über XML-Codierung und -Decodierung.

BEARBEITEN: Wenn Sie einen XML-Writer oder eine der neuen XElement-Klassen verwenden, wird diese Codierung natürlich für Sie durchgeführt. Tatsächlich können Sie den Text einfach nehmen, in eine neue XElement-Instanz einfügen und dann die String-Version (.tostring) des Elements zurückgeben. Ich habe gehört, dass SecurityElement.Escape dieselbe Aufgabe wie Ihre Dienstprogrammmethode ausführt, aber nicht viel darüber gelesen oder verwendet hat.

EDIT2: Ignoriere meinen Kommentar zu XElement, da du immer noch auf 2.0 bist

26
Kilhoffer

Microsoft AntiXss-Bibliothek AntiXssEncoder Class in System.Web.dll hat Methoden dafür:

AntiXss.XmlEncode(string s)
AntiXss.XmlAttributeEncode(string s)

es hat auch HTML:

AntiXss.HtmlEncode(string s)
AntiXss.HtmlAttributeEncode(string s)
14
Luke Quinane

In .net 3.5+

new XText("I <want> to & encode this for XML").ToString();

Gibt Ihnen:

I &lt;want&gt; to &amp; encode this for XML

Es stellt sich heraus, dass diese Methode einige Dinge nicht codiert, die sie sollte (wie Anführungszeichen).

SecurityElement.Escape ( workmad3s Antwort ) scheint dies besser zu machen, und es ist in früheren Versionen von .net enthalten.

Wenn Ihnen der Code von Drittanbietern nichts ausmacht und Sie sicherstellen möchten, dass keine unzulässigen Zeichen in Ihre XML-Datei aufgenommen werden, empfehle ich Antwort von Michael Kropat .

12
Ronnie Overby

XmlTextWriter.WriteString() erledigt die Flucht.

5
GSerg

Dies kann der Fall sein, wenn Sie von der WriteCData-Methode profitieren.

public override void WriteCData(string text)
    Member of System.Xml.XmlTextWriter

Summary:
Writes out a <![CDATA[...]]> block containing the specified text.

Parameters:
text: Text to place inside the CDATA block.

Ein einfaches Beispiel sieht folgendermaßen aus:

writer.WriteStartElement("name");
writer.WriteCData("<unsafe characters>");
writer.WriteFullEndElement();

Das Ergebnis sieht so aus:

<name><![CDATA[<unsafe characters>]]></name>

Beim Lesen der Knotenwerte entfernt der XMLReader automatisch den CData-Teil des Innertexts, damit Sie sich nicht darum kümmern müssen. Der einzige Haken ist, dass Sie die Daten als innerText-Wert in einem XML-Knoten speichern müssen. Mit anderen Worten, Sie können keinen CData-Inhalt in einen Attributwert einfügen.

3
Dscoduc

Wenn dies eine ASP.NET-App ist, warum nicht Server.HtmlEncode () verwenden?

3
Kev

Sie können die integrierte Klasse XAttribute verwenden, die die Codierung automatisch verarbeitet:

using System.Xml.Linq;

XDocument doc = new XDocument();

List<XAttribute> attributes = new List<XAttribute>();
attributes.Add(new XAttribute("key1", "val1&val11"));
attributes.Add(new XAttribute("key2", "val2"));

XElement elem = new XElement("test", attributes.ToArray());

doc.Add(elem);

string xmlStr = doc.ToString();
0
Cosmin

Wenn Sie es ernst meinen mit alle der ungültigen Zeichen (nicht nur die wenigen "html" -Zeichen) und Sie Zugriff auf System.Xml Haben, ist dies der einfachste Weg, um das richtige XML zu erstellen Codierung von Wertdaten :

string theTextToEscape = "Something \x1d else \x1D <script>alert('123');</script>";
var x = new XmlDocument();
x.LoadXml("<r/>"); // simple, empty root element
x.DocumentElement.InnerText = theTextToEscape; // put in raw string
string escapedText = x.DocumentElement.InnerXml; // Returns:  Something &#x1D; else &#x1D; &lt;script&gt;alert('123');&lt;/script&gt;

// Repeat the last 2 lines to escape additional strings.

Es ist wichtig zu wissen, dass XmlConvert.EncodeName() nicht geeignet ist, da dies für Entity-/Tag-Namen und nicht für Werte gilt. Dies zu verwenden, wäre wie eine URL-Codierung, wenn Sie eine HTML-Codierung benötigen.

0
Granger

Hier ist eine einzeilige Lösung mit den XElements. Ich benutze es in einem sehr kleinen Werkzeug. Ich brauche es kein zweites Mal, also halte ich es so. (Es ist dirdy doug)

StrVal = (<x a=<%= StrVal %>>END</x>).ToString().Replace("<x a=""", "").Replace(">END</x>", "")

Oh und es funktioniert nur in VB nicht in C #

0
Phillip

Brillant! Das ist alles was ich sagen kann.

Hier ist eine VB) Variante des aktualisierten Codes (nicht in einer Klasse, nur eine Funktion), die den XML-Code bereinigt und auch bereinigt

Function cXML(ByVal _buf As String) As String
    Dim textOut As New StringBuilder
    Dim c As Char
    If _buf.Trim Is Nothing OrElse _buf = String.Empty Then Return String.Empty
    For i As Integer = 0 To _buf.Length - 1
        c = _buf(i)
        If Entities.ContainsKey(c) Then
            textOut.Append(Entities.Item(c))
        ElseIf (AscW(c) = &H9 OrElse AscW(c) = &HA OrElse AscW(c) = &HD) OrElse ((AscW(c) >= &H20) AndAlso (AscW(c) <= &HD7FF)) _
            OrElse ((AscW(c) >= &HE000) AndAlso (AscW(c) <= &HFFFD)) OrElse ((AscW(c) >= &H10000) AndAlso (AscW(c) <= &H10FFFF)) Then
            textOut.Append(c)
        End If
    Next
    Return textOut.ToString

End Function

Shared ReadOnly Entities As New Dictionary(Of Char, String)() From {{""""c, "&quot;"}, {"&"c, "&amp;"}, {"'"c, "&apos;"}, {"<"c, "&lt;"}, {">"c, "&gt;"}}
0
nepaluz