String escape in XML


90

Esiste una funzione C # che potrebbe essere utilizzata per eseguire l'escape e annullare l'escape di una stringa, che potrebbe essere utilizzata per riempire il contenuto di un elemento XML?

Sto usando VSTS 2008 + C # + .Net 3.0.

EDIT 1: Sto concatenando file XML semplici e brevi e non uso la serializzazione, quindi ho bisogno di sfuggire esplicitamente al carattere XML a mano, ad esempio, devo metterlo a<bin <foo></foo>, quindi ho bisogno di una stringa di escape a<be metterlo nell'elemento pippo.



15
Il più breve che mi viene in mente:new XText(unescaped).ToString()
guarda il

3
Per chiunque altro inciampo su questo, ho trovato questo per essere la migliore risposta: stackoverflow.com/a/5304827/1224069
Philip Pittle

Risposte:


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

5
Non è nemmeno necessario aggiungere l'elemento al documento. Tuttavia, direi comunque che è meglio non provare a farlo in primo luogo - sembra che George stia lavorando per se stesso facendo le cose a mano ...
Jon Skeet

15
Non mi piace davvero questa risposta perché è troppo pesante. XmlDocument utilizzerà XmlReader / XmlWriter per fare il vero lavoro, quindi perché non tagliare al sodo ed evitare quel pesante DOM?
Steven Sudit

7
@ Will, l'OP ha chiesto una funzione che sfugga a un testo che potrebbe essere inserito in un elemento XML e non in un attributo. La mia funzione non sfugge alle virgolette singole o doppie perché possono essere inserite negli elementi XML.
Darin Dimitrov

5
@Darin buon punto, e uno che dovrebbe essere sottolineato. Sono soddisfatto del risultato di questa conversazione e ritiro le mie riserve. Buongiorno signore.

1
Mi chiedo se HttpUtility.HtmlEncodeda System.Webpotrebbe essere tranquillamente utilizzato?
Pooven


38

EDIT: dici "Sto concatenando file XML semplici e brevi e non uso la serializzazione, quindi ho bisogno di sfuggire esplicitamente al carattere XML a mano".

Ti consiglio vivamente di non farlo a mano. Usa le API XML per fare tutto per te: leggi i file originali, unisci i due in un unico documento come ti serve (probabilmente vorrai usare XmlDocument.ImportNode), quindi riscrivilo di nuovo. Non vuoi scrivere i tuoi parser / formattatori XML. La serializzazione è in qualche modo irrilevante qui.

Se puoi darci un esempio breve ma completo di quello che stai cercando di fare, probabilmente possiamo aiutarti a evitare di doverti preoccupare di scappare in primo luogo.


Risposta originale

Non è del tutto chiaro cosa intendi, ma normalmente le API XML lo fanno per te. Si imposta il testo in un nodo e verrà automaticamente sfuggito a tutto ciò di cui ha bisogno. Per esempio:

Esempio di LINQ to XML:

using System;
using System.Xml.Linq;

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

        Console.WriteLine(element);
    }
}

Esempio 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);
    }
}

Risultato da entrambi gli esempi:

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

Questo presuppone che tu voglia l'escape XML, ovviamente. Se non lo sei, inserisci maggiori dettagli.


Grazie Jon, ho inserito maggiori dettagli nella sezione EDIT 1 del mio post originale. Apprezzo se puoi darmi alcuni commenti e consigli. :-)
George2

"dopo l'escape XML" - intendi? Puoi parlare in altre parole per favore? L'inglese non è la mia lingua madre. :-)
George2

Ciao Jon, come annullare l'uscita dal formato XML nel normale formato stringa, cioè dall'input "Brackets & amp; stuff & lt; & gt;", otteniamo l'output "Brackets & stuff <>"?
George2

2
@ George2: chiedi a XElement il suo valore, o XmlElement per il suo InnerText.
Jon Skeet

25

Grazie a @sehe per la fuga di una riga:

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

Aggiungo ad esso l'annullamento di una riga:

var unescapedAgain = System.Xml.XmlReader.Create(new StringReader("<r>" + escaped + "</r>")).ReadElementString();

XText non sfugge alle virgolette.
Mert Gülsoy

9

George, è semplice. Utilizza sempre le API XML per gestire XML. Fanno tutte le scappatoie e non scappare per te.

Non creare mai XML aggiungendo stringhe.


Parole per vivere. Sono disponibili molte opzioni API XML, ma l'unica cosa su cui dovremmo essere tutti d'accordo è che la concatenazione manuale di stringhe non è accettabile.
Steven Sudit

Sebbene generalmente sia d'accordo con questo, potrebbero esserci alcuni casi molto rari in cui potrebbe essere necessario l'escape manuale. Ad esempio, durante la creazione della documentazione XML utilizzando Roslyn.
svick

@svick: perché non creare l'XML utilizzando LINQ to XML e quindi utilizzare .ToString ()?
John Saunders

@ JohnSaunders, perché Roslyn ha il proprio set di classi XML, come XmlElementSyntax. Ed è anche complicato dal fatto che devi generare ///anche il file. E non posso generare ogni riga come separata XObject, perché non funzionerebbe per i tag multilinea.
svick

1
@svick: quindi genera l'xml, tutto su una riga, mettilo ///davanti, quindi riformatta il codice. Non è un grosso problema, e sicuramente un caso d'angolo. Se assolutamente necessario, sono sicuro che potresti creare un'abitudine XmlWriterper fare interruzioni di riga e spazi bianchi nel modo che desideri, ma posizionandoli ///davanti a nuove righe. In alternativa, usa un XSLT per stampare in modo carino l'XML. In ogni caso, XML dovrebbe comunque essere generato da un'API XML.
John Saunders

5

E se vuoi, come me quando ho trovato questa domanda, sfuggire ai nomi dei nodi XML, come ad esempio quando leggi da una serializzazione XML, usa il modo più semplice:

XmlConvert.EncodeName(string nameToEscape)

Inoltre, sfuggirà agli spazi e a qualsiasi carattere non valido per gli elementi XML.

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


Penso, sulla base delle domande, che vogliano solo testo interno. La tua soluzione funzionerà, ma è un po 'eccessiva poiché ha lo scopo di gestire anche cose come i nomi di elementi e attributi. \
Sean Duggan

Bene, sono arrivato qui cercando di sfuggire ai nomi dei nodi e ho pensato che le mie scoperte avrebbero potuto aiutare chiunque in futuro. Inoltre non vedo qual è l '"eccessivo" ma va bene. ;)
CharlieBrown

Oh, sono informazioni utili. :) Ho solo pensato di farti notare che uno dei motivi per cui potresti non essere stato votato è perché le persone potrebbero pensare che non stai rispondendo alla domanda in questione.
Sean Duggan

Il collegamento porta a documenti per SecurityElement.Escape (String), era intenzionale? XmlConvert.EncodeName (String) ha la sua pagina. So che sono passati alcuni anni da quando è stato chiesto, ma come faccio a sapere quale usare? Non fanno la stessa cosa ma in modi diversi?
micnil

4

ATTENZIONE: Necromancing

Ancora la risposta di Darin Dimitrov + System.Security.SecurityElement.Escape (string s) non è completa.

In XML 1.1, il modo più semplice e sicuro è semplicemente codificare TUTTO.
Come &#09;per \ t.
Non è affatto supportato in XML 1.0.
Per XML 1.0, una possibile soluzione alternativa è codificare in base 64 il testo contenente i caratteri.

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

Quindi in XML 1.1, come si evita tutto?
Philip Pittle,

@Philip Pittle: See SpecialXmlEscape
Stefan Steiger

4

Un altro approccio basato sulla risposta di John Skeet che non restituisce i tag :

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

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

Restituisce solo il valore passato, in formato codificato XML:

Brackets &amp; stuff &lt;&gt; and "quotes"

3

Le seguenti funzioni faranno il lavoro. Non è stato eseguito il test con XmlDocument, ma immagino che sia molto più veloce.

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

3

Utilizzando una libreria di terze parti ( Newtonsoft.Json ) come alternativa:

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

Esempio:

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

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.