Экранировать недопустимые символы XML в C #

84

У меня есть строка, содержащая недопустимые символы XML. Как я могу избежать (или удалить) недопустимые символы XML перед синтаксическим анализом строки?

Алиреза Нури
источник
2
Не могли бы вы предоставить больше контекста? Пример ввода и пример ожидаемого вывода. Также что вы собираетесь делать с выводом.
Дарин Димитров
5
Вы пишете XML? Или вы пытаетесь читать XML, который на самом деле не XML?
Марк Гравелл
3
Используйте XmlWriter, он избавит вас от недопустимых символов
Thomas Levesque
2
@alireza, вы получите больше полезных ответов, если ответите на вопросы, которые вам задают (для получения дополнительной информации) здесь, в комментариях ...
Марк Гравелл
Мне жаль. Я отсутствовал на несколько часов. Пожалуйста, прочтите вопрос, который привел к этому: stackoverflow.com/questions/8330619/. Вы получите всю необходимую информацию там,
Алиреза Нури

Ответы:

113

В качестве способа удаления недопустимых символов XML я предлагаю вам использовать метод XmlConvert.IsXmlChar . Он был добавлен с .NET Framework 4 и также представлен в Silverlight. Вот небольшой образец:

void Main() {
    string content = "\v\f\0";
    Console.WriteLine(IsValidXmlString(content)); // False

    content = RemoveInvalidXmlChars(content);
    Console.WriteLine(IsValidXmlString(content)); // True
}

static string RemoveInvalidXmlChars(string text) {
    var validXmlChars = text.Where(ch => XmlConvert.IsXmlChar(ch)).ToArray();
    return new string(validXmlChars);
}

static bool IsValidXmlString(string text) {
    try {
        XmlConvert.VerifyXmlChars(text);
        return true;
    } catch {
        return false;
    }
}

И как способ избежать недопустимых символов XML я предлагаю вам использовать метод XmlConvert.EncodeName . Вот небольшой образец:

void Main() {
    const string content = "\v\f\0";
    Console.WriteLine(IsValidXmlString(content)); // False

    string encoded = XmlConvert.EncodeName(content);
    Console.WriteLine(IsValidXmlString(encoded)); // True

    string decoded = XmlConvert.DecodeName(encoded);
    Console.WriteLine(content == decoded); // True
}

static bool IsValidXmlString(string text) {
    try {
        XmlConvert.VerifyXmlChars(text);
        return true;
    } catch {
        return false;
    }
}

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

Игорь Кустов
источник
XmlConvert.VerifyXmlCharsне генерирует исключение, если аргумент содержит недопустимые символы, он возвращает пустую строку (и возвращает аргумент, если все содержащиеся символы допустимы). Просто попробуй return XmlConvert.VerifyXmlChars (text) != null.
Мэтт Энрайт
3
@IgorKustov Моя плохая! Документация по возвращаемому значению, кажется, противоречит этому, спасибо, что поймали меня.
Мэтт Энрайт,
3
Осторожно, не используйте XmlConvert.EncodeName, если строка предназначена для значения XML. Ограничения имени XML строже, чем ограничения значения XML, и кодирование имени приведет к ненужному неожиданному экранированию.
Дэвид Бург
1
@arik мой код служит только для демонстрации, чтобы показать состояние строки XML до и после преобразования. Очевидно, что в вашем коде вам не нужно его проверять.
Игорь Кустов
67

Используйте SecurityElement.Escape

using System;
using System.Security;

class Sample {
  static void Main() {
    string text = "Escape characters : < > & \" \'";
    string xmlText = SecurityElement.Escape(text);
//output:
//Escape characters : &lt; &gt; &amp; &quot; &apos;
    Console.WriteLine(xmlText);
  }
}
BLUEPIXY
источник
11
Это не экранирует управляющие символы (например, char 30).
zimdanen
19

Если вы пишете xml, просто используйте классы, предоставляемые фреймворком, для создания xml. Вам не придется беспокоиться о побеге или чем-то еще.

Console.Write(new XElement("Data", "< > &"));

Будет выводить

<Data>&lt; &gt; &amp;</Data>

Если вам нужно прочитать XML-файл, который имеет неправильный формат, не используйте регулярное выражение. Вместо этого используйте Html Agility Pack .

Пьер-Ален Вижан
источник
Ницца. У вас есть эквивалентный метод для тех, кто использует XmlElement?
djdanlib
3
Обновление: установка свойства XmlElement InnerText, похоже, правильно ускользает. Ответил на свой вопрос, ура!
djdanlib
Итак, ваш xml сформирован неправильно? нравится <Data>&</Data>?
Пьер-Ален Вижан 01
2
Да, именно в этом и проблема.
Алиреза Нури
2
У вас все еще могут возникнуть проблемы, если содержимое ваших элементов содержит недопустимые символы, такие как backspace (0x08), многие другие управляющие символы или суррогатные кодовые точки.
jakubiszon
6

Метод RemoveInvalidXmlChars, предоставленный Irishman, не поддерживает суррогатные символы. Чтобы проверить это, используйте следующий пример:

static void Main()
{
    const string content = "\v\U00010330";

    string newContent = RemoveInvalidXmlChars(content);

    Console.WriteLine(newContent);
}

Это возвращает пустую строку, но этого не должно быть! Он должен вернуть «\ U00010330», потому что символ U + 10330 является допустимым символом XML.

Для поддержки суррогатных символов я предлагаю использовать следующий метод:

public static string RemoveInvalidXmlChars(string text)
{
    if (string.IsNullOrEmpty(text))
        return text;

    int length = text.Length;
    StringBuilder stringBuilder = new StringBuilder(length);

    for (int i = 0; i < length; ++i)
    {
        if (XmlConvert.IsXmlChar(text[i]))
        {
            stringBuilder.Append(text[i]);
        }
        else if (i + 1 < length && XmlConvert.IsXmlSurrogatePair(text[i + 1], text[i]))
        {
            stringBuilder.Append(text[i]);
            stringBuilder.Append(text[i + 1]);
            ++i;
        }
    }

    return stringBuilder.ToString();
}
Франсуа С
источник
4

Вот оптимизированная версия вышеупомянутого метода RemoveInvalidXmlChars, который не создает новый массив при каждом вызове, что излишне нагружает GC:

public static string RemoveInvalidXmlChars(string text)
{
    if (text == null)
        return text;
    if (text.Length == 0)
        return text;

    // a bit complicated, but avoids memory usage if not necessary
    StringBuilder result = null;
    for (int i = 0; i < text.Length; i++)
    {
        var ch = text[i];
        if (XmlConvert.IsXmlChar(ch))
        {
            result?.Append(ch);
        }
        else if (result == null)
        {
            result = new StringBuilder();
            result.Append(text.Substring(0, i));
        }
    }

    if (result == null)
        return text; // no invalid xml chars detected - return original text
    else
        return result.ToString();

}
Урс Мейли
источник
Что это за ?.синтаксис? в очереди result?.Append(ch);?
JB. С Моникой.
1
?.это Null-Conditional Operator. docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
Pure.Krome
1
// Replace invalid characters with empty strings.
   Regex.Replace(inputString, @"[^\w\.@-]", ""); 

Шаблон регулярного выражения [^ \ w. @ -] соответствует любому символу, который не является символом слова, точкой, знаком @ или дефисом. Символ слова - это любая буква, десятичная цифра или знак препинания, например подчеркивание. Любой символ, соответствующий этому шаблону, заменяется на String.Empty, которая является строкой, определенной шаблоном замены. Чтобы разрешить ввод дополнительных символов пользователем, добавьте эти символы в класс символов в шаблоне регулярного выражения. Например, шаблон регулярного выражения [^ \ w. @ - \%] также допускает использование символа процента и обратной косой черты во входной строке.

Regex.Replace(inputString, @"[!@#$%_]", "");

Обратитесь и к этому:

Удаление недопустимых символов из тега имени XML - RegEx C #

Вот функция для удаления символов из указанной строки XML:

using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace XMLUtils
{
    class Standards
    {
        /// <summary>
        /// Strips non-printable ascii characters 
        /// Refer to http://www.w3.org/TR/xml11/#charsets for XML 1.1
        /// Refer to http://www.w3.org/TR/2006/REC-xml-20060816/#charsets for XML 1.0
        /// </summary>
        /// <param name="content">contents</param>
        /// <param name="XMLVersion">XML Specification to use. Can be 1.0 or 1.1</param>
        private void StripIllegalXMLChars(string tmpContents, string XMLVersion)
        {    
            string pattern = String.Empty;
            switch (XMLVersion)
            {
                case "1.0":
                    pattern = @"#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|7F|8[0-46-9A-F]9[0-9A-F])";
                    break;
                case "1.1":
                    pattern = @"#x((10?|[2-F])FFF[EF]|FDD[0-9A-F]|[19][0-9A-F]|7F|8[0-46-9A-F]|0?[1-8BCEF])";
                    break;
                default:
                    throw new Exception("Error: Invalid XML Version!");
            }

            Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
            if (regex.IsMatch(tmpContents))
            {
                tmpContents = regex.Replace(tmpContents, String.Empty);
            }
            tmpContents = string.Empty;
        }
    }
}
Шива Чаран
источник
0
string XMLWriteStringWithoutIllegalCharacters(string UnfilteredString)
{
    if (UnfilteredString == null)
        return string.Empty;

    return XmlConvert.EncodeName(UnfilteredString);
}

string XMLReadStringWithoutIllegalCharacters(string FilteredString)
{
    if (UnfilteredString == null)
        return string.Empty;

    return XmlConvert.DecodeName(UnfilteredString);
}

Этот простой метод заменяет недопустимые символы тем же значением, но принимается в контексте XML.


Для записи строки используйте XMLWriteStringWithoutIllegalCharacters (строка UnfilteredString).
Для чтения строки используйте XMLReadStringWithoutIllegalCharacters (строка FilteredString).

Марко Конкас
источник