Domanda

Stavo cercando un metodo generico in .Net per codificare una stringa da usare in un elemento o attributo Xml e sono rimasto sorpreso quando non l'ho trovato immediatamente. Quindi, prima di andare troppo oltre, potrei semplicemente perdere la funzione integrata?

Supponendo per un momento che non esista davvero, sto mettendo insieme il mio metodo EncodeForXml(string data) generico e sto pensando al modo migliore per farlo.

I dati che sto usando che hanno spinto l'intera faccenda potrebbero contenere caratteri cattivi come & amp ;, < ;, " ;, ecc. Potrebbe anche contenere a volte le entità correttamente sfuggite: & amp; amp ;, & amp; lt ;, e & amp; quot ;, il che significa che usare una sezione CDATA potrebbe non essere la migliore idea. Sembra un po 'klunky comunque; Preferirei piuttosto finire con un bel valore di stringa che può essere usato direttamente nel file XML.

Ho usato un'espressione regolare in passato per catturare solo e commerciali cattive, e sto pensando di usarla per catturarle in questo caso, così come il primo passo, e quindi fare un semplice rimpiazzo per altri personaggi.

Quindi, questo potrebbe essere ulteriormente ottimizzato senza renderlo troppo complesso, e c'è qualcosa che mi manca? :

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

Mi dispiace per tutti voi C # -solo gente-- Non mi interessa davvero quale lingua io uso, ma volevo rendere Regex statico e non potete farlo in C # senza dichiararlo al di fuori del metodo , quindi questo sarà VB.Net

Infine, siamo ancora su .Net 2.0 dove lavoro, ma se qualcuno potesse prendere il prodotto finale e trasformarlo in un metodo di estensione per la classe di stringhe, sarebbe anche bello.

Aggiorna Le prime risposte indicano che .Net ha davvero dei modi integrati per farlo. Ma ora che ho iniziato, voglio finire il mio metodo EncodeForXml () solo per divertirmi, quindi sto ancora cercando idee per migliorare. In particolare: un elenco più completo di caratteri che dovrebbero essere codificati come entità (forse memorizzati in un elenco / mappa) e qualcosa che ottiene prestazioni migliori rispetto a un .Replace () su stringhe immutabili in serie.

È stato utile?

Soluzione

System.XML gestisce la codifica per te, quindi non hai bisogno di un metodo come questo.

Altri suggerimenti

A seconda di quanto sai dell'input, potresti dover tenere conto del fatto che non tutti i caratteri Unicode sono caratteri XML validi .

Sia Server.HtmlEncode che System.Security.SecurityElement.Escape sembrano ignorare i caratteri XML illegali, mentre System.XML.XmlWriter.WriteString genera un ArgumentException quando incontra caratteri illegali (a meno che non si disabiliti tale segno di spunta nel qual caso li ignora). È disponibile una panoramica delle funzioni della libreria qui .

Modifica 2011/8/14: visto che almeno alcune persone hanno consultato questa risposta negli ultimi due anni, ho deciso di riscrivere completamente il codice originale, che presentava numerosi problemi, tra cui < a href = "https://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful"> orribilmente maltrattato 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
}

Test unitari e codice completo sono disponibili qui .

SecurityElement.Escape

documentato qui

In passato ho usato HttpUtility.HtmlEncode per codificare il testo per XML. Svolge lo stesso compito, davvero. Non ho ancora riscontrato alcun problema, ma questo non vuol dire che non lo farò in futuro. Come suggerisce il nome, è stato creato per HTML, non per XML.

Probabilmente l'hai già letto, ma qui è un articolo sulla codifica e decodifica XML.

EDIT: ovviamente, se usi un xmlwriter o una delle nuove classi XElement, questa codifica è fatta per te. In effetti, potresti semplicemente prendere il testo, inserirlo in una nuova istanza XElement, quindi restituire la versione stringa (.tostring) dell'elemento. Ho sentito che SecurityElement.Escape eseguirà lo stesso compito del metodo di utilità, ma non ha mai letto molto o l'ha usato.

EDIT2: ignora il mio commento su XElement, dato che sei ancora su 2.0

di Microsoft Libreria AntiXss Classe AntiXssEncoder in System.Web. dll ha metodi per questo:

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

ha anche HTML:

AntiXss.HtmlEncode(string s)
AntiXss.HtmlAttributeEncode(string s)

In .net 3.5+

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

Ti dà:

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

Si scopre che questo metodo non codifica alcune cose che dovrebbe (come le virgolette).

SecurityElement.Escape ( la risposta di workmad3 ) sembra fare un lavoro migliore con questo ed è incluso nelle versioni precedenti di. net.

Se non ti dispiace il codice di terze parti e vuoi assicurarti che nessun carattere illegale arrivi nel tuo XML, ti consiglio di di Michael Kropat rispondere .

XmlTextWriter.WriteString() esegue la fuga.

Se si tratta di un'app ASP.NET, perché non utilizzare Server.HtmlEncode ()?

Questo potrebbe essere il caso in cui potresti trarre vantaggio dall'uso del metodo WriteCData.

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.

Un semplice esempio sarebbe simile al seguente:

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

Il risultato è simile a:

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

Durante la lettura dei valori del nodo, XMLReader elimina automaticamente la parte CData del testo interno, quindi non devi preoccuparti. L'unico problema è che devi archiviare i dati come valore innerText in un nodo XML. In altre parole, non è possibile inserire il contenuto di CData in un valore di attributo.

Brilliant! Questo è tutto ciò che posso dire.

Ecco una variante VB del codice aggiornato (non in una classe, solo una funzione) che pulirà e disinfetterà anche l'xml

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;"}}

Puoi usare la classe integrata XAttribute , che gestisce automaticamente la codifica:

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

Ecco una soluzione a linea singola che utilizza XElements. Lo uso in uno strumento molto piccolo. Non ne ho bisogno una seconda volta, quindi continuo così. (Il suo sporco doug)

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

Oh, funziona solo in VB, non in C #

Se sei seriamente intenzionato a gestire tutti dei caratteri non validi (non solo i pochi " html " quelli) e hai accesso a System.Xml, ecco il modo più semplice per eseguire la codifica Xml corretta di dati di valore :

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.

È importante sapere che XmlConvert.EncodeName() non è appropriato, poiché si tratta di nomi di entità / tag, non di valori. Usarlo sarebbe come la codifica URL quando era necessario codificare HTML.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top