String escape em XML

90

Existe alguma função C # que poderia ser usada para escapar e cancelar o escape de uma string, que poderia ser usada para preencher o conteúdo de um elemento XML?

Estou usando o VSTS 2008 + C # + .Net 3.0.

EDIT 1: Estou concatenando um arquivo XML simples e curto e não uso a serialização, então preciso escapar explicitamente o caractere XML à mão, por exemplo, preciso colocar a<bem <foo></foo>, portanto, preciso escapar da string a<be colocá-la no elemento foo.

George2
fonte
15
O mais curto que consigo pensar:new XText(unescaped).ToString()
ver
3
Para qualquer pessoa que se deparar com isso, descobri que esta é a melhor resposta: stackoverflow.com/a/5304827/1224069
Philip Pittle

Respostas:

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
fonte
5
Você nem precisa acrescentar o elemento ao documento. No entanto, eu ainda diria que é melhor não tentar fazer isso em primeiro lugar - parece que George está trabalhando para si mesmo fazendo as coisas manualmente ...
Jon Skeet
15
Eu realmente não gosto dessa resposta porque é muito pesada. XmlDocument vai usar XmlReader / XmlWriter para fazer o trabalho real, então por que não ir direto ao ponto e evitar esse DOM pesado?
Steven Sudit
7
@Will, o OP pediu uma função que escapasse de um texto que poderia ser colocado em um elemento XML e não em um atributo. Minha função não escapa de aspas simples ou duplas porque elas podem ser colocadas em elementos XML.
Darin Dimitrov,
5
@darin bom ponto, e que deve ser enfatizado. Estou satisfeito com o resultado desta conversa e retiro minhas reservas. Bom Dia senhor.
1
Eu me pergunto se o HttpUtility.HtmlEncodefrom System.Webpoderia ser usado com segurança?
Pooven
126

SecurityElement.Escape (string s)

Dana Holt
fonte
8
Esta resposta escapa das citações, ao contrário da resposta selecionada.
1
Esta resposta não parece funcionar com caracteres inválidos como
Haacked
16
E como você recupera?
Gondy,
2
Esta resposta está incompleta. Ele responde apenas a metade da pergunta.
Brian Webster,
1
Concordar com os comentários acima - incompletos e não 100% precisos.
G. Stoynev
38

EDIT: Você diz "Estou concatenando um arquivo XML simples e curto e não uso a serialização, então preciso escapar explicitamente do caractere XML manualmente".

Eu recomendo fortemente que você não faça isso manualmente. Use as APIs XML para fazer tudo por você - leia os arquivos originais, mescle os dois em um único documento da maneira que for necessário (provavelmente você deseja usar XmlDocument.ImportNode) e, em seguida, escreva novamente. Você não quer escrever seus próprios analisadores / formatadores XML. A serialização é um tanto irrelevante aqui.

Se você puder nos dar um exemplo breve, mas completo, de exatamente o que está tentando fazer, provavelmente podemos ajudá-lo a evitar a preocupação de escapar em primeiro lugar.


Resposta original

Não está totalmente claro o que você quer dizer, mas normalmente APIs XML fazem isso para você. Você define o texto em um nó e ele automaticamente escapará de tudo o que for necessário. Por exemplo:

Exemplo de LINQ to XML:

using System;
using System.Xml.Linq;

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

        Console.WriteLine(element);
    }
}

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

Resultado de ambos os exemplos:

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

Isso presumindo que você deseja escape de XML, é claro. Se não estiver, poste mais detalhes.

Jon Skeet
fonte
Obrigado Jon, coloquei mais detalhes na seção EDIT 1 do meu post original. Agradeço se você pudesse me dar alguns comentários e conselhos. :-)
George2
"após o escape de XML" - você quer dizer? Você poderia falar em outras palavras, por favor? Inglês não é minha língua nativa. :-)
George2
Olá Jon, como cancelar o escape do formato XML para o formato de string normal, ou seja, da entrada "Brackets & amp; stuff & lt; & gt;", obtemos a saída "Brackets & stuff <>"?
George2
2
@ George2: Você solicita ao XElement seu valor ou ao XmlElement seu InnerText.
Jon Skeet
25

Agradecimentos a @sehe pelo escape de uma linha:

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

Acrescento a ele o un-escape de uma linha:

var unescapedAgain = System.Xml.XmlReader.Create(new StringReader("<r>" + escaped + "</r>")).ReadElementString();
Keith Robertson
fonte
XText não escapa as aspas.
Mert Gülsoy
9

George, é simples. Sempre use as APIs XML para lidar com XML. Eles fazem todas as fugas e unescaping para você.

Nunca crie XML anexando strings.

John Saunders
fonte
Palavras para viver. Existem muitas opções de API XML disponíveis, mas a única coisa com a qual todos devemos concordar é que a concatenação manual de strings não é aceitável.
Steven Sudit
Embora eu geralmente concorde com isso, pode haver alguns casos muito raros em que o escape manual pode ser necessário. Por exemplo, ao criar documentação XML usando Roslyn.
svick
@svick: por que não criar o XML usando LINQ to XML e, em seguida, usar .ToString ()?
John Saunders
@JohnSaunders, porque Roslyn tem seu próprio conjunto de classes XML, como XmlElementSyntax. E também é complicado pelo fato de que você precisa gerar o ///também. E não posso gerar cada linha separadamente XObject, porque isso não funcionaria para tags de várias linhas .
svick
1
@svick: então gere o xml, tudo em uma linha, cole ///na frente dele e reformate o código. Não é um grande problema, e certamente um caso secundário. Se for absolutamente necessário, tenho certeza que você pode criar um costume XmlWriterpara fazer quebras de linha e espaços em branco da maneira que desejar, mas colocando ///na frente de novas linhas. Como alternativa, use um XSLT para fazer uma impressão bonita do XML. Mas, em qualquer caso, o XML ainda deve ser gerado por uma API XML.
John Saunders
5

E se você quiser, como eu quando encontrei esta pergunta, escapar dos nomes de nós XML, como por exemplo ao ler de uma serialização XML, use a maneira mais fácil:

XmlConvert.EncodeName(string nameToEscape)

Ele também escapará de espaços e quaisquer caracteres inválidos para elementos XML.

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

Charlie Brown
fonte
Acho, com base nas perguntas, que eles querem apenas um texto interno. Sua solução funcionará, mas é um tanto exagerada, pois se destina a também lidar com coisas como nomes de elementos e atributos. \
Sean Duggan
Bem, eu cheguei aqui tentando escapar de qualquer coisa de nomes de nó e pensei que minhas descobertas poderiam ajudar alguém no futuro. Eu também não vejo o que é "exagero", mas está tudo bem. ;)
CharlieBrown de
Oh, é uma informação útil. :) Acabei de descobrir que devo apontar que um dos motivos pelos quais você pode não ter sido votado é porque as pessoas podem achar que você não está respondendo à pergunta em questão.
Sean Duggan
O link leva a documentos para SecurityElement.Escape (String), isso foi intencional? XmlConvert.EncodeName (String) tem sua própria página. Eu sei que já se passaram alguns anos desde que isso foi perguntado, mas como posso saber qual usar? Eles não fazem a mesma coisa, mas de maneiras diferentes?
micnil de
4

AVISO: Necromante

Ainda assim, a resposta de Darin Dimitrov + System.Security.SecurityElement.Escape (string s) não está completa.

No XML 1.1, a maneira mais simples e segura é apenas codificar TUDO.
Gostar &#09;de \ t.
Não tem suporte em XML 1.0.
Para XML 1.0, uma solução possível é codificar em base 64 o texto que contém o (s) caractere (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
fonte
Então, no XML 1.1, como você escapa de tudo?
Philip Pittle
@Philip Pittle: Ver SpecialXmlEscape
Stefan Steiger
4

Outra abordagem baseada na resposta de John Skeet que não retorna as tags :

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

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

Isso retorna apenas o valor passado, em formato codificado em XML:

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

As funções a seguir farão o trabalho. Não testei com o XmlDocument, mas acho que é muito mais rápido.

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
fonte
3

Usando uma biblioteca de terceiros ( Newtonsoft.Json ) como 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();
}

Exemplo:

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

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

Abberdeen
fonte