Chaîne d'échappement dans XML

90

Existe-t-il une fonction C # qui pourrait être utilisée pour échapper et déséchapper une chaîne, qui pourrait être utilisée pour remplir le contenu d'un élément XML?

J'utilise VSTS 2008 + C # + .Net 3.0.

EDIT 1: Je suis concaténer simple et fichier XML court et je ne l' utilise sérialisation, donc je dois échapper explicitement le caractère XML à la main, par exemple, je dois mettre a<ben <foo></foo>, donc j'ai besoin échapper corde a<bet de le mettre dans l' élément foo.

George2
la source
15
Shortest Je peux penser à:new XText(unescaped).ToString()
sehe
3
Pour tous ceux qui tombent dessus, j'ai trouvé que c'était la meilleure réponse: stackoverflow.com/a/5304827/1224069
Philip Pittle

Réponses:

74
public static string XmlEscape(string unescaped)
{
    XmlDocument doc = new XmlDocument();
    XmlNode node = doc.CreateElement("root");
    node.InnerText = unescaped;
    return node.InnerXml;
}

public static string XmlUnescape(string escaped)
{
    XmlDocument doc = new XmlDocument();
    XmlNode node = doc.CreateElement("root");
    node.InnerXml = escaped;
    return node.InnerText;
}
Darin Dimitrov
la source
5
Vous n'avez même pas besoin d'ajouter l'élément au document. Cependant, je dirais quand même qu'il vaut mieux ne pas essayer de faire cela en premier lieu - on dirait que George travaille pour lui-même en faisant les choses à la main ...
Jon Skeet
15
Je n'aime vraiment pas cette réponse car elle est trop lourde. XmlDocument va utiliser XmlReader / XmlWriter pour faire le vrai travail, alors pourquoi ne pas aller droit au but et éviter ce DOM lourd?
Steven Sudit
7
@Will, l'OP a demandé une fonction qui échappera à un texte qui pourrait être placé dans un élément XML et non dans un attribut. Ma fonction n'échappe pas aux guillemets simples ou doubles car ils peuvent être placés dans des éléments XML.
Darin Dimitrov
5
@darin bon point, et qui mérite d'être souligné. Je suis satisfait du résultat de cette conversation et retire mes réserves. Bonne journée Monsieur.
1
Je me demande si HttpUtility.HtmlEncodede System.Webpourrait être utilisé en toute sécurité?
Pooven
126

SecurityElement.Escape (chaîne s)

Dana Holt
la source
8
Cette réponse échappe aux guillemets, contrairement à la réponse sélectionnée.
1
Cette réponse ne semble pas fonctionner avec des caractères invalides comme
Haacked
16
Et comment pouvez-vous vous échapper?
Gondy
2
Cette réponse est incomplète. Cela ne répond qu'à la moitié de la question.
Brian Webster
1
D'accord avec les commentaires ci-dessus - incomplets et pas précis à 100%.
G.Stoynev
38

EDIT: Vous dites "Je concatène un fichier XML simple et court et je n'utilise pas de sérialisation, donc j'ai besoin d'échapper explicitement au caractère XML à la main".

Je vous déconseille fortement de le faire à la main. Utilisez les API XML pour tout faire pour vous - lisez les fichiers d'origine, fusionnez les deux en un seul document comme vous en avez besoin (vous voulez probablement l'utiliser XmlDocument.ImportNode), puis réécrivez-le. Vous ne voulez pas écrire vos propres analyseurs / formateurs XML. La sérialisation est quelque peu hors de propos ici.

Si vous pouvez nous donner un exemple bref mais complet de ce que vous essayez de faire exactement, nous pouvons probablement vous aider à éviter d'avoir à vous soucier de vous échapper en premier lieu.


Réponse originale

Ce que vous voulez dire n'est pas tout à fait clair, mais normalement les API XML le font pour vous. Vous définissez le texte dans un nœud, et il échappera automatiquement à tout ce dont il a besoin. Par exemple:

Exemple LINQ to XML:

using System;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        XElement element = new XElement("tag",
                                        "Brackets & stuff <>");

        Console.WriteLine(element);
    }
}

Exemple DOM:

using System;
using System.Xml;

class Test
{
    static void Main()
    {
        XmlDocument doc = new XmlDocument();
        XmlElement element = doc.CreateElement("tag");
        element.InnerText = "Brackets & stuff <>";
        Console.WriteLine(element.OuterXml);
    }
}

Résultat des deux exemples:

<tag>Brackets &amp; stuff &lt;&gt;</tag>

Cela suppose que vous vouliez que XML s'échappe, bien sûr. Si ce n'est pas le cas, veuillez publier plus de détails.

Jon Skeet
la source
Merci Jon, j'ai mis plus de détails dans mon article original EDIT 1 section. Merci si vous pouviez me donner quelques commentaires et conseils. :-)
George2
"après l'échappement XML" - vous voulez dire? Pourriez-vous parler en d'autres termes s'il vous plaît? L'anglais n'est pas ma langue maternelle. :-)
George2
Salut Jon, comment déséchapper du format XML au format de chaîne normal, c'est-à-dire de l'entrée "Brackets & amp; stuff & lt; & gt;", nous obtenons la sortie "Brackets & stuff <>"?
George2
2
@ George2: Vous demandez au XElement sa valeur ou au XmlElement son InnerText.
Jon Skeet
25

Merci à @sehe pour l'évasion en une ligne:

var escaped = new System.Xml.Linq.XText(unescaped).ToString();

J'ajoute à cela le un-escape d'une ligne:

var unescapedAgain = System.Xml.XmlReader.Create(new StringReader("<r>" + escaped + "</r>")).ReadElementString();
Keith Robertson
la source
XText n'échappe pas les guillemets.
Mert Gülsoy
9

George, c'est simple. Utilisez toujours les API XML pour gérer XML. Ils font tout ce qui vous échappe et ne vous échappe pas.

Ne créez jamais de XML en ajoutant des chaînes.

John Saunders
la source
Des mots à vivre. Il existe de nombreuses options d'API XML disponibles, mais la seule chose sur laquelle nous devrions tous nous mettre d'accord est que la concaténation manuelle de chaînes n'est pas acceptable.
Steven Sudit
Bien que je sois généralement d'accord avec cela, il peut y avoir de très rares cas où un échappement manuel peut être nécessaire. Par exemple, lors de la création de documentation XML à l'aide de Roslyn.
svick
@svick: pourquoi ne pas créer le XML en utilisant LINQ to XML, puis utiliser .ToString ()?
John Saunders
@JohnSaunders, car Roslyn a son propre ensemble de classes XML, comme XmlElementSyntax. Et c'est aussi compliqué par le fait que vous devez aussi générer le ///. Et je ne peux pas générer chaque ligne séparément XObject, car cela ne fonctionnerait pas pour les balises multilignes.
svick
1
@svick: alors générez le xml, le tout sur une seule ligne, collez-le ///devant, puis reformatez le code. Ce n'est pas un gros problème, et certainement une affaire de coin. Si c'est absolument nécessaire, je suis sûr que vous pouvez créer une personnalisation XmlWriterpour faire des sauts de ligne et des espaces comme vous le souhaitez, mais en les plaçant ///devant de nouvelles lignes. Vous pouvez également utiliser un XSLT pour imprimer correctement le XML. Mais dans tous les cas, XML doit toujours être généré par une API XML.
John Saunders
5

Et si vous voulez, comme moi quand j'ai trouvé cette question, échapper aux noms de nœuds XML, comme par exemple lors de la lecture à partir d'une sérialisation XML, utilisez le moyen le plus simple:

XmlConvert.EncodeName(string nameToEscape)

Il échappera également les espaces et tous les caractères non valides pour les éléments XML.

http://msdn.microsoft.com/en-us/library/system.security.securityelement.escape%28VS.80%29.aspx

Charlie Brown
la source
Je pense, sur la base des questions, qu'ils veulent juste du texte intérieur. Votre solution fonctionnera, mais est un peu exagérée car elle est destinée à gérer également des choses comme les noms d'éléments et d'attributs. \
Sean Duggan
Eh bien, je suis arrivé ici en essayant d'échapper aux noms de nœuds et j'ai pensé que mes découvertes pourraient aider n'importe qui à l'avenir. Je ne vois pas non plus quel est le "surpuissance" mais c'est OK. ;)
CharlieBrown
Oh, ce sont des informations utiles. :) J'ai juste pensé que je soulignerais que l'une des raisons pour lesquelles vous n'avez peut-être pas voté était parce que les gens pourraient penser que vous ne répondez pas à la question en question.
Sean Duggan
Le lien mène à des documents pour SecurityElement.Escape (String), était-ce intentionnel? XmlConvert.EncodeName (String) a sa propre page. Je sais que cela fait quelques années que cela a été demandé, mais comment savoir lequel utiliser? Ne font-ils pas la même chose mais de manières différentes?
micnil
4

AVERTISSEMENT: Nécromance

La réponse de Darin Dimitrov + System.Security.SecurityElement.Escape (string s) n'est toujours pas complète.

Dans XML 1.1, le moyen le plus simple et le plus sûr est de simplement encoder TOUT.
Comme &#09;pour \ t.
Il n'est pas du tout pris en charge dans XML 1.0.
Pour XML 1.0, une solution de contournement possible consiste à encoder en base 64 le texte contenant le (s) caractère (s).

//string EncodedXml = SpecialXmlEscape("привет мир");
//Console.WriteLine(EncodedXml);
//string DecodedXml = XmlUnescape(EncodedXml);
//Console.WriteLine(DecodedXml);
public static string SpecialXmlEscape(string input)
{
    //string content = System.Xml.XmlConvert.EncodeName("\t");
    //string content = System.Security.SecurityElement.Escape("\t");
    //string strDelimiter = System.Web.HttpUtility.HtmlEncode("\t"); // XmlEscape("\t"); //XmlDecode("&#09;");
    //strDelimiter = XmlUnescape("&#59;");
    //Console.WriteLine(strDelimiter);
    //Console.WriteLine(string.Format("&#{0};", (int)';'));
    //Console.WriteLine(System.Text.Encoding.ASCII.HeaderName);
    //Console.WriteLine(System.Text.Encoding.UTF8.HeaderName);


    string strXmlText = "";

    if (string.IsNullOrEmpty(input))
        return input;


    System.Text.StringBuilder sb = new StringBuilder();

    for (int i = 0; i < input.Length; ++i)
    {
        sb.AppendFormat("&#{0};", (int)input[i]);
    }

    strXmlText = sb.ToString();
    sb.Clear();
    sb = null;

    return strXmlText;
} // End Function SpecialXmlEscape

XML 1.0:

public static string Base64Encode(string plainText)
{
    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
    return System.Convert.ToBase64String(plainTextBytes);
}

public static string Base64Decode(string base64EncodedData)
{
    var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
    return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
Stefan Steiger
la source
Alors en XML 1.1, comment échapper à tout?
Philip Pittle
@Philip Pittle: Voir SpecialXmlEscape
Stefan Steiger
4

Une autre prise basée sur la réponse de John Skeet qui ne renvoie pas les balises :

void Main()
{
    XmlString("Brackets & stuff <> and \"quotes\"").Dump();
}

public string XmlString(string text)
{
    return new XElement("t", text).LastNode.ToString();
} 

Cela renvoie uniquement la valeur transmise, au format encodé XML:

Brackets &amp; stuff &lt;&gt; and "quotes"
Rick Strahl
la source
3

Les fonctions suivantes feront le travail. Je n'ai pas testé contre XmlDocument, mais je suppose que c'est beaucoup plus rapide.

public static string XmlEncode(string value)
{
    System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings 
    {
        ConformanceLevel = System.Xml.ConformanceLevel.Fragment
    };

    StringBuilder builder = new StringBuilder();

    using (var writer = System.Xml.XmlWriter.Create(builder, settings))
    {
        writer.WriteString(value);
    }

    return builder.ToString();
}

public static string XmlDecode(string xmlEncodedValue)
{
    System.Xml.XmlReaderSettings settings = new System.Xml.XmlReaderSettings
    {
        ConformanceLevel = System.Xml.ConformanceLevel.Fragment
    };

    using (var stringReader = new System.IO.StringReader(xmlEncodedValue))
    {
        using (var xmlReader = System.Xml.XmlReader.Create(stringReader, settings))
        {
            xmlReader.Read();
            return xmlReader.Value;
        }
    }
}
Ramazan Binarbasi
la source
3

Utilisation d'une bibliothèque tierce ( Newtonsoft.Json ) comme alternative:

public static string XmlEncode(string unescaped)
{
    if (unescaped == null) return null;
    return JsonConvert.SerializeObject(unescaped); ;
}

public static string XmlDecode(string escaped)
{
    if (escaped == null) return null;
    return JsonConvert.DeserializeObject(escaped, typeof(string)).ToString();
}

Exemple:

a<b <==> "a&lt;b"

<foo></foo> <==> "foo&gt;&lt;/foo&gt;"

abberdeen
la source