72 votes

Meilleure façon de coder des données texte pour XML

Je cherchais une méthode générique .Net pour encoder une chaîne pour une utilisation dans un Xml de l'élément ou de l'attribut, et a été surpris quand je n'ai pas immédiatement en trouver un. Donc, avant d'aller trop loin, je pourrais juste être à côté de la fonction intégrée?

En supposant pour un instant qu'il n'existe pas, je suis en train de constituer ma propre générique EncodeForXml(string data) méthode, et je suis en train de réfléchir à la meilleure façon de le faire.

Les données que je suis sur que vous y êtes invité toute cette affaire pourrait contenir de mauvais caractères &, <, ", etc. Il pourrait aussi contient, à l'occasion, les échappées entités: &amp;, &lt; et &quot;, ce qui signifie simplement à l'aide d'une section CDATA peut-être pas la meilleure idée. Cela semble un peu klunky anyay; je préfère de loin jusqu'à la fin avec une belle chaîne de valeur qui peut être utilisée directement dans le xml.

J'ai utilisé une expression régulière dans le passé et il suffit de prendre de mauvaises arobases, et je pense que je vais l'utiliser pour les attraper dans ce cas, ainsi que de la première étape, et ensuite de faire un simple de remplacer pour les autres personnages.

Donc, cela pourrait être optimisé en outre, sans le rendre trop complexe, et est-ce que je suis absent? :

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 vous tous, C# -seulement les gens-- je n'ai pas vraiment de soins de la langue que j'utilise, mais je voulais faire la Regex statique et vous ne pouvez pas le faire en C# sans la déclarer en dehors de la méthode, ce sera donc VB.Net

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

Mise à jour de La première quelques réponses indiquent que .Net, en effet, ont intégré dans les façons de le faire. Mais maintenant que j'ai commencé, je veux finir mes EncodeForXml() la méthode juste pour le fun, donc je suis toujours à la recherche d'idées pour l'améliorer. Notamment: une liste plus complète de caractères qui doit être codé comme des entités (peut-être stockée dans une liste ou une carte), et quelque chose qui obtient de meilleures performances que d'en faire un .Replace() sur les cordes immuables dans la série.

79voto

Michael Kropat Points 3993

Selon combien vous en savez sur l'entrée, vous pouvez avoir à prendre en compte le fait que pas tous les caractères Unicode sont des caractères XML valides.

Les Deux Serveur.HtmlEncode et du Système.De sécurité.SecurityElement.Échapper semblent ignorer illégale des caractères XML, tout Système.XML.XmlWriter.WriteString déclenche une exception d'argument lorsqu'il rencontre des caractères illégaux (sauf si vous désactivez cette case, auquel cas, il l'ignore). Une vue d'ensemble des fonctions de la bibliothèque est disponible ici.

Edit 2011/8/14: de voir qu'au moins quelques personnes ont consulté cette réponse dans les deux dernières années, j'ai décidé de réécrire complètement le code d'origine, qui a eu de nombreux problèmes, y compris horriblement mauvaise manipulation de l'UTF-16.

/// <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
}

Les tests unitaires et le code complet peut être trouvé ici.

33voto

workmad3 Points 12974

SecurityElement.Escape

documenté ici

27voto

Kilhoffer Points 13430

Dans le passé, j'ai utilisé HttpUtility.HtmlEncode pour encoder du texte pour xml. Il effectue la même tâche, vraiment. Je n'ai pas couru dans des problèmes avec elle, mais cela ne veut pas dire que je ne vais pas à l'avenir. Comme son nom l'indique, elle a été faite pour le code HTML, pas XML.

Vous avez probablement déjà lu, mais voici un article sur le langage xml d'encodage et de décodage.

EDIT: bien sûr, si vous utilisez un xmlwriter ou de l'un des XElement classes, ce jeu est fait pour vous. En fait, vous pouvez simplement prendre le texte, de le placer dans un nouveau XElement instance, puis revenir à la chaîne.tostring) version de l'élément. J'ai entendu dire que SecurityElement.Échapper à la va effectuer la même tâche que votre utilitaire méthode, mais nai pas lu beaucoup sur le sujet ou de s'en servir.

EDIT2: Ignorer mon commentaire à propos de XElement, puisque vous êtes toujours sur la 2.0

11voto

Ronnie Overby Points 11402

Dans .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

S'avère que cette méthode n'a pas d'encoder des choses qu'il devrait (comme les guillemets).

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

Si vous n'avez pas l'esprit de la 3e partie du code et veulent s'assurer qu'aucun des caractères illégaux dans votre XML, je vous recommande de Michael Kropat de réponse.

4voto

MusiGenesis Points 49273

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X