Преобразование строки в XML

92

Есть ли какая-либо функция C #, которая может использоваться для экранирования и отмены экранирования строки, которая может использоваться для заполнения содержимого элемента XML?

Я использую VSTS 2008 + C # + .Net 3.0.

EDIT 1: Я конкатенации простой и короткий файл XML , и я не использую сериализации, так что мне нужно явно избежать XML характер вручную, например, мне нужно положить a<bв <foo></foo>, так что мне нужно бежать строку a<bи поместить его в элемент обув.

Георгий2
источник
Ни одного способа, но вот несколько: http://weblogs.sqlteam.com/mladenp/archive/2008/10/21/Different-ways-how-to-escape-an-XML-string-in-C .aspx
marcc
15
Самый короткий, который я могу придумать:new XText(unescaped).ToString()
см.
3
Для всех, кто наткнулся на это, я считаю, что это лучший ответ: stackoverflow.com/a/5304827/1224069
Филип Питтл,

Ответы:

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
Вам даже не нужно добавлять элемент в документ. Тем не менее, я бы все же сказал, что лучше не пытаться делать это в первую очередь - похоже, что Джордж делает работу для себя, делая что-то вручную ...
Джон Скит
15
Мне очень не нравится этот ответ, потому что он слишком тяжелый. XmlDocument будет использовать XmlReader / XmlWriter для выполнения реальной работы, так почему бы не перейти к делу и не избежать этого тяжелого DOM?
Стивен Судит,
7
@Will OP запросил функцию, которая будет экранировать текст, который можно было бы поместить в элемент XML, а не в атрибут. Моя функция не избегает одинарных или двойных кавычек, потому что их можно поместить в элементы XML.
Дарин Димитров
5
@darin - хороший аргумент, и его следует подчеркнуть. Я доволен результатом этого разговора и снимаю свои оговорки. Хорошего дня, сэр.
1
Интересно, можно ли безопасно использовать HttpUtility.HtmlEncodefrom System.Web?
Pooven
127

SecurityElement.Escape (строка)

Дана Холт
источник
9
В этом ответе нет кавычек, в отличие от выбранного ответа.
2
Этот ответ, похоже, не работает с недопустимыми символами, такими как
16
И как тебе сбежать?
Gondy
2
Это неполный ответ. Это отвечает только на половину вопроса.
Брайан Вебстер,
1
Согласен с приведенными выше комментариями - неполные и не на 100% точные.
Г. Стойнев
38

РЕДАКТИРОВАТЬ: вы говорите: «Я объединяю простой и короткий XML-файл, и я не использую сериализацию, поэтому мне нужно явно вручную экранировать символ XML».

Я настоятельно не рекомендую делать это вручную. Используйте XML API, чтобы сделать все за вас - прочтите исходные файлы, объедините их в один документ, но вам это нужно (вы, вероятно, захотите использоватьXmlDocument.ImportNode ), а затем снова запишите его. Вы не хотите писать свои собственные анализаторы / средства форматирования XML. Сериализация здесь несколько неактуальна.

Если вы дадите нам краткий, но полный пример того, что именно вы пытаетесь сделать, мы, вероятно, сможем помочь вам вообще не беспокоиться о побеге.


Оригинальный ответ

Не совсем понятно, что вы имеете в виду, но обычно XML API делают это за вас. Вы устанавливаете текст в узле, и он автоматически избегает всего, что ему нужно. Например:

Пример LINQ to XML:

using System;
using System.Xml.Linq;

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

        Console.WriteLine(element);
    }
}

Пример 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);
    }
}

Результат обоих примеров:

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

Это, конечно, при условии, что вы хотите экранировать XML. Если нет, опубликуйте подробности.

Джон Скит
источник
Спасибо, Джон, я поместил более подробную информацию в свой исходный раздел EDIT 1. Буду признателен, если вы дадите мне несколько комментариев и советов. :-)
George2
Вы имеете в виду "после экранирования XML"? Не могли бы вы сказать другими словами? Английский не мой родной язык. :-)
George2
Привет, Джон, как выйти из формата XML в нормальный строковый формат, т.е. из ввода «Скобки и прочее & lt; & gt;», мы получим выход «Скобки и прочее <>»?
George2
2
@ George2: вы запрашиваете у XElement его значение или у XmlElement для его InnerText.
Джон Скит,
25

Спасибо @sehe за однострочный переход:

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

Я добавляю к нему однострочную отмену:

var unescapedAgain = System.Xml.XmlReader.Create(new StringReader("<r>" + escaped + "</r>")).ReadElementString();
Кейт Робертсон
источник
XText не экранирует кавычки.
Mert Gülsoy
9

Джордж, это просто. Всегда используйте XML API для обработки XML. Они все убегают за вас.

Никогда не создавайте XML, добавляя строки.

Джон Сондерс
источник
Слова чтобы жить. Доступно множество вариантов XML API, но мы все должны согласиться с тем, что объединение строк вручную недопустимо.
Стивен Судит,
Хотя я в целом согласен с этим, могут быть очень редкие случаи, когда может потребоваться экранирование вручную. Например, при создании XML-документации с помощью Roslyn.
svick
@svick: почему бы не создать XML с помощью LINQ to XML, а затем использовать .ToString ()?
Джон Сондерс
@JohnSaunders, потому что у Roslyn есть собственный набор классов XML, например XmlElementSyntax. И это также осложняется тем, что вам ///тоже нужно сгенерировать . И я не могу генерировать каждую строку как отдельную XObject, потому что это не сработает для многострочных тегов.
svick
1
@svick: поэтому сгенерируйте xml в одной строке, вставьте ///перед ним, а затем переформатируйте код. Ничего особенного, и, конечно же, очень важный случай. В случае крайней необходимости, я уверен, вы могли бы создать обычай, XmlWriterчтобы делать разрывы строк и пробелы так, как вы хотите, но помещая их ///перед новыми строками. В качестве альтернативы можно использовать XSLT для красивой печати XML. Но в любом случае XML должен по-прежнему генерироваться XML API.
Джон Сондерс
6

И если вы хотите, как и я, когда я нашел этот вопрос, избежать имен узлов XML, например, при чтении из сериализации XML, используйте самый простой способ:

XmlConvert.EncodeName(string nameToEscape)

Он также будет экранировать пробелы и любые недопустимые символы для элементов XML.

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

Чарли Браун
источник
Я думаю, исходя из вопросов, им просто нужен внутренний текст. Ваше решение будет работать, но оно несколько избыточно, поскольку оно также предназначено для обработки таких вещей, как имена элементов и атрибутов. \
Шон Дагган,
1
Ну, я пришел сюда, пытаясь избежать каких-либо имен узлов, и подумал, что мои выводы могут помочь кому-нибудь в будущем. Я тоже не понимаю, что такое "перебор", но все в порядке. ;)
CharlieBrown
О, это полезная информация. :) Я просто подумал, что хочу указать, что одна из причин, по которой вы могли не получить одобрение, заключалась в том, что люди могли подумать, что вы не отвечаете на поставленный вопрос.
Шон Дагган
Ссылка ведет к документации для SecurityElement.Escape (String), это было намеренно? XmlConvert.EncodeName (String) имеет собственную страницу. Я знаю, что прошло несколько лет с тех пор, как этот вопрос был задан, но как мне узнать, какой из них использовать? Разве они не делают то же самое, но по-разному?
micnil
@CharlieBrown: Может быть, вы также хотите создать из него отдельный вопрос и ответить на него, чтобы люди могли лучше его найти. Спасибо, что разместили!
Флориан Штрауб,
5

Еще один вариант, основанный на ответе Джона Скита, который не возвращает теги :

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

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

Это возвращает только переданное значение в кодированном формате XML:

Brackets &amp; stuff &lt;&gt; and "quotes"
Рик Страл
источник
4

ВНИМАНИЕ: Некромантинг

Тем не менее ответ Дарина Димитрова + System.Security.SecurityElement.Escape (string s) не завершен.

В XML 1.1 самый простой и безопасный способ - просто закодировать ВСЕ.
Как &#09;для \ т.
Он вообще не поддерживается в XML 1.0.
Для XML 1.0 одним из возможных обходных путей является кодирование текста, содержащего символ (символы), в кодировке base-64.

//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);
}
Стефан Штайгер
источник
Итак, как избежать всего в XML 1.1?
Филип Питтл
@Philip Pittle: См. SpecialXmlEscape
Stefan Steiger
3

Следующие функции сделают свою работу. Не тестировал XmlDocument, но думаю, это намного быстрее.

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

Использование сторонней библиотеки ( Newtonsoft.Json ) в качестве альтернативы:

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

Пример:

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

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

Аббердин
источник