Ciąg znaków ucieczki do XML

92

Czy istnieje funkcja C #, której można użyć do zmiany znaczenia i cofnięcia zmiany znaczenia ciągu, który mógłby zostać użyty do wypełnienia zawartości elementu XML?

Używam VSTS 2008 + C # + .Net 3.0.

EDIT 1: jestem łącząc prosty i krótki plik XML i nie używam serializacji, więc trzeba jawnie uciec charakter XML ręcznie, na przykład, muszę umieścić a<bw <foo></foo>, więc muszę uciec ciąg a<bi umieścić go w elemencie foo.

George2
źródło
15
Najkrótszy, jaki przychodzi mi do new XText(unescaped).ToString()
głowy
3
Dla każdego, kto się na to
natknie

Odpowiedzi:

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
źródło
5
Nie musisz nawet dołączać elementu do dokumentu. Jednak nadal powiedziałbym, że najlepiej nie próbować tego robić w pierwszej kolejności - brzmi to tak, jakby George pracował dla siebie, robiąc rzeczy ręcznie ...
Jon Skeet
15
Naprawdę nie podoba mi się ta odpowiedź, ponieważ jest zbyt ciężka. XmlDocument użyje XmlReader / XmlWriter do prawdziwej pracy, więc dlaczego nie przejść do sedna i uniknąć tego ciężkiego DOM?
Steven Sudit
7
@Will, OP poprosił o funkcję, która będzie usuwać tekst, który można umieścić w elemencie XML , a nie w atrybucie. Moja funkcja nie wymyka się apostrofom ani cudzysłowom, ponieważ można je umieszczać w elementach XML.
Darin Dimitrov
5
@darin to dobra uwaga, którą należy podkreślić. Jestem zadowolony z wyniku tej rozmowy i wycofuję zastrzeżenia. Dzień dobry panu.
1
Zastanawiam się, czy można bezpiecznie użyć HttpUtility.HtmlEncodefrom System.Web?
Pooven
127

SecurityElement.Escape (ciągi)

Dana Holt
źródło
9
Ta odpowiedź wymyka się cudzysłowom, w przeciwieństwie do wybranej odpowiedzi.
2
Wydaje się, że ta odpowiedź nie działa z nieprawidłowymi znakami, takimi jak
Haacked
16
A jak uciekasz?
Gondy
2
Ta odpowiedź jest niepełna. Odpowiada tylko na połowę pytania.
Brian Webster
1
Zgadzam się z powyższymi komentarzami - niekompletne i nie w 100% dokładne.
G. Stoynev,
38

EDYCJA: Mówisz "Łączę prosty i krótki plik XML i nie używam serializacji, więc muszę jawnie ręcznie zmienić znaczenie znaków XML".

Chciałbym zdecydowanie radzimy nie robić to ręcznie. Użyj interfejsów API XML, aby zrobić to wszystko za siebie - wczytaj oryginalne pliki, połącz je w jeden dokument, jakkolwiek potrzebujesz (prawdopodobnie chcesz użyćXmlDocument.ImportNode ), a następnie ponownie zapisz. Nie chcesz pisać własnych parserów / elementów formatujących XML. Serializacja jest tutaj nieco nieistotna.

Jeśli możesz nam podać krótki, ale kompletny przykład tego, co dokładnie próbujesz zrobić, prawdopodobnie pomożemy Ci uniknąć martwienia się o ucieczkę.


Oryginalna odpowiedź

Nie jest do końca jasne, co masz na myśli, ale zwykle interfejsy API XML robią to za Ciebie. Ustawiasz tekst w węźle, a on automatycznie zmieni wszystko, czego potrzebuje. Na przykład:

Przykład LINQ to XML:

using System;
using System.Xml.Linq;

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

        Console.WriteLine(element);
    }
}

Przykład 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);
    }
}

Dane wyjściowe z obu przykładów:

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

Oczywiście przy założeniu, że chcesz uciekać przed XML-em. Jeśli nie, prześlij więcej szczegółów.

Jon Skeet
źródło
Dzięki Jon, umieściłem więcej szczegółów w mojej oryginalnej sekcji EDIT 1. Będę wdzięczny za udzielenie mi kilku uwag i porad. :-)
George2
„po ucieczce XML” - masz na myśli? Czy mógłbyś powiedzieć innymi słowami? Angielski nie jest moim językiem ojczystym. :-)
George2
Cześć Jon, jak cofnąć wyjście z formatu XML do normalnego formatu ciągu, tj. Z wejścia „Nawiasy i elementy & lt; & gt;”, otrzymujemy wynik „Nawiasy i elementy <>”?
George2,
2
@ George2: Pytasz XElement o jego wartość lub XmlElement o jego InnerText.
Jon Skeet,
26

Dzięki @sehe za jedną linię ucieczki:

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

Dodaję do tego jedną linijkę un-escape:

var unescapedAgain = System.Xml.XmlReader.Create(new StringReader("<r>" + escaped + "</r>")).ReadElementString();
Keith Robertson
źródło
XText nie usuwa cudzysłowów.
Mert Gülsoy
9

George, to proste. Zawsze używaj interfejsów API XML do obsługi XML. Robią dla ciebie wszystkie ucieczki i odskakiwanie.

Nigdy nie twórz kodu XML przez dołączanie ciągów.

John Saunders
źródło
Słowa, którymi warto żyć. Dostępnych jest wiele opcji interfejsu API XML, ale wszyscy powinniśmy się zgodzić, że ręczne łączenie ciągów znaków jest niedopuszczalne.
Steven Sudit
Chociaż generalnie się z tym zgadzam, mogą wystąpić bardzo rzadkie przypadki, w których może być konieczna ręczna ucieczka. Na przykład podczas tworzenia dokumentacji XML przy użyciu Roslyn.
svick
@svick: dlaczego nie utworzyć XML za pomocą LINQ to XML, a następnie użyć .ToString ()?
John Saunders
@JohnSaunders, ponieważ Roslyn ma własny zestaw klas XML, takich jak XmlElementSyntax. Jest to również skomplikowane przez fakt, że musisz wygenerować ///też. I nie mogę wygenerować każdego wiersza jako osobnego XObject, ponieważ to nie zadziała w przypadku tagów wielowierszowych.
svick
1
@svick: więc wygeneruj xml, wszystko w jednym wierszu, umieść ///przed nim, a następnie sformatuj ponownie kod. Nie jest to wielka sprawa, a na pewno bardzo narożna sprawa. Jeśli jest to absolutnie konieczne, jestem pewien, że możesz utworzyć niestandardowe XmlWriterpodziały wierszy i białe znaki tak, jak chcesz, ale umieszczając je ///przed nowymi wierszami. Alternatywnie, użyj XSLT, aby dobrze wydrukować XML. Jednak w każdym przypadku XML powinien być nadal generowany przez XML API.
John Saunders
6

A jeśli chcesz, tak jak ja, kiedy znalazłem to pytanie, uciec od nazw węzłów XML, jak na przykład podczas czytania z serializacji XML, użyj najprostszego sposobu:

XmlConvert.EncodeName(string nameToEscape)

Będzie również usuwał spacje i wszelkie nieprawidłowe znaki dla elementów XML.

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

Charlie Brown
źródło
Opierając się na pytaniach, myślę, że chcą tylko wewnętrznego tekstu. Twoje rozwiązanie będzie działać, ale jest nieco przesadzone, ponieważ ma obsługiwać takie rzeczy, jak nazwy elementów i atrybutów. \
Sean Duggan
1
Cóż, przybyłem tutaj, próbując uciec przed nazwami węzłów i pomyślałem, że moje odkrycia mogą pomóc każdemu w przyszłości. Nie rozumiem też, co to za „przesada”, ale jest OK. ;)
CharlieBrown
Och, to przydatne informacje. :) Pomyślałem, że zwróciłbym uwagę, że jednym z powodów, dla których mogłeś nie zostać pozytywnie ocenionym, było to, że ludzie mogą czuć, że nie odpowiadasz na zadane pytanie.
Sean Duggan,
Odsyłacz prowadzi do dokumentów dotyczących SecurityElement.Escape (String), czy było to zamierzone? XmlConvert.EncodeName (String) ma własną stronę. Wiem, że minęło kilka lat, odkąd o to zapytano, ale skąd mam wiedzieć, którego użyć? Czy nie robią tego samego, ale na różne sposoby?
micnil
@CharlieBrown: Może chcesz również utworzyć z niego oddzielne pytanie i odpowiedzieć na nie, aby ludzie mogli je lepiej znaleźć. Dzięki za wysłanie!
Florian Straub
6

Inne ujęcie oparte na odpowiedzi Johna Skeeta, które nie zwraca tagów :

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

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

Zwraca tylko wartość przekazaną w formacie XML:

Brackets &amp; stuff &lt;&gt; and "quotes"
Rick Strahl
źródło
4

OSTRZEŻENIE: Nekromancja

Wciąż odpowiedź Darina Dimitrova + System.Security.SecurityElement.Escape (string s) nie jest kompletna.

W XML 1.1 najprostszym i najbezpieczniejszym sposobem jest po prostu zakodowanie WSZYSTKIEGO.
Jak &#09;dla \ t.
Nie jest w ogóle obsługiwany w XML 1.0.
W przypadku XML 1.0 jednym możliwym obejściem jest kodowanie base-64 tekstu zawierającego znaki.

//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
źródło
Jak więc w XML 1.1 uniknąć wszystkiego?
Philip Pittle
@Philip Pittle: Zobacz SpecialXmlEscape
Stefan Steiger
3

Następujące funkcje będą działać. Nie testowałem XmlDocument, ale myślę, że jest to znacznie szybsze.

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
źródło
3

Korzystanie z biblioteki innej firmy ( Newtonsoft.Json ) jako alternatywy:

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

Przykład:

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

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

abberdeen
źródło