Question

Je recherchais une méthode générique dans .Net pour coder une chaîne à utiliser dans un élément ou un attribut XML, et j'ai été surprise de ne pas en avoir trouvé une immédiatement. Donc, avant d’aller trop loin, est-ce que je pourrais juste manquer la fonction intégrée?

Supposant un instant que cela n’existe pas vraiment, j’assemble ma propre méthode générique EncodeForXml(string data), et je réfléchis à la meilleure façon de procéder.

Les données que j'utilise et qui indiquent que tout cela peut contenir des caractères incorrects tels que & amp ;, < ;, & ";, etc. peuvent également contenir les entités correctement échappées: & amp; amp ;, & amp; lt ;, et & amp; quot ;, ce qui signifie que le simple fait d'utiliser une section CDATA n'est peut-être pas la meilleure idée. Cela semble un peu klunky de toute façon; Je préférerais de beaucoup me retrouver avec une belle valeur de chaîne pouvant être utilisée directement dans le XML.

Dans le passé, j’ai utilisé une expression régulière uniquement pour capturer les mauvais esperluettes, et je pense l’utiliser pour les capturer dans ce cas ainsi que dans la première étape, puis effectuer un remplacement simple pour les autres caractères.

Alors, est-ce que ceci pourrait être optimisé davantage sans le rendre trop complexe, et y at-il quelque chose qui me manque? :

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

Désolé pour tout ce que vous faites en C # - uniquement des gens - je ne me soucie pas vraiment de la langue que j'utilise, mais je voulais rendre le Regex statique et vous ne pouvez pas le faire en C # sans le déclarer en dehors de la méthode , ce sera donc VB.Net

Enfin, nous travaillons toujours sur .Net 2.0, mais si quelqu'un pouvait prendre le produit final et le transformer en une méthode d'extension pour la classe string, ce serait très cool aussi.

Mise à jour Les premières réponses indiquent que .Net dispose effectivement de méthodes intégrées pour le faire. Mais maintenant que j'ai commencé, je veux plutôt finir ma méthode EncodeForXml () juste pour le plaisir, alors je suis toujours à la recherche d'idées d'amélioration. Notamment: une liste plus complète de caractères qui devraient être encodés en tant qu’entités (peut-être stockées dans une liste / carte), et quelque chose qui offre de meilleures performances que de faire un .Replace () sur des chaînes immuables en série.

Était-ce utile?

La solution

System.XML gère l'encodage pour vous, vous n'avez donc pas besoin d'une méthode comme celle-ci.

Autres conseils

En fonction de vos connaissances sur l'entrée, vous devrez peut-être prendre en compte le not tous les caractères Unicode sont des caractères XML valides .

Server.HtmlEncode et System.Security.SecurityElement.Escape semblent ignorer les caractères XML illégaux, tandis que System.XML.XmlWriter.WriteString lève une ArgumentException lorsqu'il rencontre des caractères non autorisés (à moins que vous ne désactiviez cette vérification, auquel cas il les ignore). Une présentation des fonctions de la bibliothèque est disponible ici .

Edit 2011/8/14: voyant qu'au moins quelques personnes avaient consulté cette réponse au cours des deux dernières années, j'ai décidé de réécrire complètement le code d'origine, qui posait de nombreux problèmes, notamment < a href = "https://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful"> horriblement mal gérée 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
}
Des tests unitaires et le code complet sont disponibles ici . .

SecurityElement.Escape

documenté ici

Dans le passé, j’utilisais HttpUtility.HtmlEncode pour coder du texte pour XML. Il effectue la même tâche, vraiment. Je n'ai encore rencontré aucun problème avec cela, mais cela ne veut pas dire que je ne le ferai plus à l'avenir. Comme son nom l'indique, il a été conçu pour HTML, pas pour XML.

Vous l'avez probablement déjà lu, mais , voici un article sur le codage et le décodage XML.

EDIT: Bien sûr, si vous utilisez un rédacteur XML ou l’une des nouvelles classes XElement, cet encodage est fait pour vous. En fait, vous pouvez simplement prendre le texte, le placer dans une nouvelle instance de XElement, puis renvoyer la version chaîne (.tostring) de l'élément. J'ai entendu dire que SecurityElement.Escape effectuera également la même tâche que votre méthode d’utilitaire, mais n’aura pas lu beaucoup de choses à ce sujet et ne l’a pas utilisé.

EDIT2: Ne tenez pas compte de mon commentaire sur XElement, car vous êtes toujours sur la version 2.0

La Microsoft < bibliothèque AntiXss Classe AntiXssEncoder dans System.Web. dll a des méthodes pour cela:

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

il a aussi HTML:

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

En .net 3.5 +

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

vous donne:

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

Il s'avère que cette méthode ne code pas certaines choses qu'elle devrait (comme des guillemets).

SecurityElement.Escape ( réponse de workmad3 ) semble faire un meilleur travail avec cela et il est inclus dans les versions antérieures de. net.

Si le code de tiers ne vous dérange pas et que vous souhaitez vous assurer qu'aucun caractère illégal ne figure dans votre code XML, je vous recommanderais Michael Kropat's répondre .

XmlTextWriter.WriteString() fait l'échappement.

S'il s'agit d'une application ASP.NET, pourquoi ne pas utiliser Server.HtmlEncode ()?

Cela pourrait être le cas où vous pourriez tirer profit de l'utilisation de la méthode 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 exemple simple ressemblerait à ceci:

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

Le résultat est le suivant:

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

Lors de la lecture des valeurs de nœud, XMLReader supprime automatiquement la partie CData du innertext afin que vous n'ayez pas à vous en préoccuper. Le seul problème est que vous devez stocker les données en tant que valeur innerText sur un nœud XML. En d'autres termes, vous ne pouvez pas insérer de contenu CData dans une valeur d'attribut.

Brillant! C'est tout ce que je peux dire.

Voici une variante VB du code mis à jour (pas dans une classe, mais une fonction) qui nettoiera et nettoiera également le 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;"}}

Vous pouvez utiliser la classe intégrée XAttribute , qui gère automatiquement le codage:

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

Voici une solution monoligne utilisant les XElements. Je l'utilise dans un très petit outil. Je n'en ai pas besoin une seconde fois, je le garde ainsi. (Son sale doug)

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

Oh, et cela ne fonctionne qu'en VB, pas en C #

Si vous envisagez sérieusement de gérer tous les caractères non valides (pas seulement les quelques & "; html &";), et si vous avez accès à System.Xml, voici le moyen le plus simple de faire un codage XML correct des données de valeur :

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.

Il est important de savoir que XmlConvert.EncodeName() n'est pas approprié, car il s'agit de noms d'entités / de balises et non de valeurs. Ce serait comme si vous utilisiez le codage HTML pour utiliser le codage URL.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top