Meilleur moyen d’encoder des données texte pour XML

Je cherchais une méthode générique dans .Net pour encoder une chaîne à utiliser dans un élément ou un atsortingbut XML, et j’ai été surpris de ne pas en trouver immédiatement une. Donc, avant d’aller plus loin, pourrais-je simplement manquer la fonction intégrée?

En supposant un instant que cela n’existe pas vraiment, je EncodeForXml(ssortingng data) ma propre EncodeForXml(ssortingng data) générique EncodeForXml(ssortingng data) et je pense à la meilleure façon de le faire.

Les données que j’ai utilisées qui ont incité tout cela pourraient contenir des caractères incorrects tels que &, <, ", etc. Il pourrait également contenir à l'occasion les entités correctement échappées: & amp ;, & lt ;, et & quot; La section CDATA n'est peut-être pas la meilleure idée, ce qui semble un peu compliqué, mais je préférerais avoir une belle valeur de chaîne pouvant être utilisée directement dans le fichier XML.

Dans le passé, j’ai utilisé une expression régulière pour capturer les mauvaises esperluètes, et je pense à l’utiliser pour les capturer dans ce cas, ainsi que pour la première étape, puis pour remplacer les autres caractères.

Donc, cela pourrait-il être optimisé sans le rendre trop complexe, et y a-t-il quelque chose qui me manque? :

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

Désolé pour tout ce que vous avez à dire, je ne me soucie pas vraiment de la langue que j’utilise, mais je voulais que le Regex soit statique et que vous ne puissiez pas le faire en C # sans le déclarer en dehors de la méthode. .Net

Enfin, nous sums toujours sur .Net 2.0 où je travaille, mais si quelqu’un pouvait prendre le produit final et le transformer en une méthode d’extension pour la classe de chaînes, ce serait vraiment cool.

Mise à jour Les premières réponses indiquent que .Net dispose effectivement de moyens pour y parvenir. Mais maintenant que j’ai commencé, je veux en quelque sorte finir ma méthode EncodeForXml () juste pour le plaisir, donc je cherche toujours des idées d’amélioration. Notamment: une liste plus complète de caractères à encoder en tant qu’entités (peut-être stockées dans une liste / map), et quelque chose qui obtient de meilleures performances que de faire un .Replace () sur des chaînes immuables en série.

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

Selon votre connaissance de l’entrée, vous devrez peut-être prendre en compte le fait que tous les caractères Unicode ne sont pas des caractères XML valides .

Server.HtmlEncode et System.Security.SecurityElement.Escape semblent ignorer tous les caractères XML illégaux, tandis que System.XML.XmlWriter.WriteSsortingng lève une exception ArgumentException lorsqu’il rencontre des caractères non autorisés (à moins que vous ne désactiviez cette vérification). Un aperçu des fonctions de la bibliothèque est disponible ici .

Edit 2011/8/14: vu qu’au moins quelques personnes ont consulté cette réponse au cours des deux dernières années, j’ai décidé de réécrire complètement le code original, qui comportait de nombreux problèmes, y compris une gestion horriblement incorrecte de UTF-16 .

 using System; using System.Collections.Generic; using System.IO; using System.Linq; ///  /// Encodes data so that it can be safely embedded as text in XML documents. ///  public class XmlTextEncoder : TextReader { public static ssortingng Encode(ssortingng s) { using (var stream = new SsortingngReader(s)) using (var encoder = new XmlTextEncoder(stream)) { return encoder.ReadToEnd(); } } /// The data to be encoded in UTF-16 format. /// 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. public XmlTextEncoder(TextReader source, bool filterIllegalChars=true) { _source = source; _filterIllegalChars = filterIllegalChars; } readonly Queue _buf = new Queue(); 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) { // Ssortingngs 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 Entities = new Dictionary { { '"', """ }, { '&', "&"}, { '\'', "'" }, { '< ', "<" }, { '>', ">" }, }; // 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 peuvent être trouvés ici .

SecurityElement.Escape

documenté ici

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 encore rencontré de problèmes, mais cela ne veut pas dire que je ne le ferai pas à l’avenir. Comme son nom l’indique, il a été créé pour HTML et non pour XML.

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

EDIT: Bien sûr, si vous utilisez un xmlwriter ou 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 de chaîne (.tossortingng) de l’élément. J’ai entendu dire que SecurityElement.Escape exécutera également la même tâche que votre méthode utilitaire, mais ne l’a pas beaucoup lu ou utilisé.

EDIT2: ignore mon commentaire sur XElement, puisque vous êtes toujours sur 2.0

La bibliothèque AntiXssEncoder de Microsoft dans la bibliothèque System.Web.dll contient des méthodes pour cela:

 AntiXss.XmlEncode(ssortingng s) AntiXss.XmlAtsortingbuteEncode(ssortingng s) 

il a aussi du HTML:

 AntiXss.HtmlEncode(ssortingng s) AntiXss.HtmlAtsortingbuteEncode(ssortingng s) 

Dans .net 3.5+

 new XText("I  to & encode this for XML").ToSsortingng(); 

Vous donne:

I <want> to & encode this for XML

Il s’avère que cette méthode ne code pas certaines choses (comme les guillemets).

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

Si cela ne vous dérange pas du code tiers et que vous voulez vous assurer qu’aucun caractère illégal ne figure dans votre XML, je recommanderais la réponse de Michael Kropat .

XmlTextWriter.WriteSsortingng() effectue l’échappement.

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

Cela peut être le cas lorsque vous pouvez utiliser la méthode WriteCData.

 public override void WriteCData(ssortingng 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(""); writer.WriteFullEndElement(); 

Le résultat ressemble à:

 < ![CDATA[]]> 

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

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

Voici une variante VB du code mis à jour (pas dans une classe, juste une fonction) qui va nettoyer et aussi nettoyer le xml

 Function cXML(ByVal _buf As Ssortingng) As Ssortingng Dim textOut As New SsortingngBuilder Dim c As Char If _buf.Trim Is Nothing OrElse _buf = Ssortingng.Empty Then Return Ssortingng.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, """}, {"&"c, "&"}, {"'"c, "'"}, {"<"c, "<"}, {">"c, ">"}} 

Vous pouvez utiliser la classe intégrée XAtsortingbute , qui gère automatiquement l’encodage:

 using System.Xml.Linq; XDocument doc = new XDocument(); List atsortingbutes = new List(); atsortingbutes.Add(new XAtsortingbute("key1", "val1&val11")); atsortingbutes.Add(new XAtsortingbute("key2", "val2")); XElement elem = new XElement("test", atsortingbutes.ToArray()); doc.Add(elem); ssortingng xmlStr = doc.ToSsortingng(); 

Voici une solution à une seule ligne utilisant les XElements. Je l’utilise dans un très petit outil. Je n’en ai pas besoin une seconde fois alors je le garde de cette façon. (Son dirdy doug)

 StrVal = (>END).ToSsortingng().Replace("END", "") 

Oh et ça ne marche que dans VB pas dans C #

Si vous êtes sérieux au sujet de la gestion de tous les caractères non valides (pas seulement les quelques “html”), et que vous avez access à System.Xml , voici le moyen le plus simple de procéder à un encodage XML correct des données de valeur :

 ssortingng theTextToEscape = "Something \x1d else \x1D "; var x = new XmlDocument(); x.LoadXml(""); // simple, empty root element x.DocumentElement.InnerText = theTextToEscape; // put in raw ssortingng ssortingng escapedText = x.DocumentElement.InnerXml; // Returns: Something  else  <script>alert('123');</script> // Repeat the last 2 lines to escape additional ssortingngs. 

Il est important de savoir que XmlConvert.EncodeName() n’est pas approprié, car il s’agit de noms d’entités / balises, pas de valeurs. Utiliser cela serait comme un encodage URL lorsque vous avez besoin de coder HTML.