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


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

6 61

6 ответов:

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

void Main() {
    string content = "\v\f";
    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";
    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;
    }
}

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

использовать SecurityElement.Побег

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

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

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

выводит

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

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

метод 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();
}

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

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

    }
// Replace invalid characters with empty strings.
   Regex.Replace(inputString, @"[^\w\.@-]", ""); 

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

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

см это тоже:

удаление недопустимых символов из XML Name Tag-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;
        }
    }
}