Уникнути недійсних символів XML у C #


83

У мене є рядок, що містить недійсні символи XML. Як я можу уникнути (або видалити) недійсні символи XML, перш ніж проаналізувати рядок?


2
Не могли б ви надати більше контексту? Зразок введення та зразок очікуваного виходу. Також, що ви маєте намір робити з результатом.
Дарін Димитров

5
Ви пишете XML? Або ви намагаєтесь прочитати XML, який насправді не є XML?
Марк Гравелл

3
Використовуйте XmlWriter, він уникне недійсних символів для вас
Томас Левеск,

2
@alireza ви отримаєте більше корисних відповідей, якщо відповісте на запитання, які вам задають люди (для отримання додаткової інформації) тут, у коментарях ...
Марк Гравелл

Вибачте. Мене не було на кілька годин. Будь ласка, прочитайте питання, яке призвело до цього: stackoverflow.com/questions/8330619/... Ви отримаєте всю необхідну інформацію там
Alireza Noori

Відповіді:


112

Як спосіб видалення недійсних символів 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.
Matt Enright


3
@IgorKustov Мій поганий! Документація про повернене значення, здається, суперечить цьому, дякую, що вигнали мене.
Метт Енрайт,

3
Обережно, щоб не використовувати XmlConvert.EncodeName, якщо рядок призначений для значення XML. Обмеження імен XML суворіші, ніж обмеження значень XML і кодування імен призведе до непотрібних несподіваних виходів.
Девід Бург

1
@arik мій код служить лише демонстраційною метою, щоб показати стан рядка XML до і після перетворення. Очевидно, що у коді вам не потрібно перевіряти його.
Ігор Кустов

66

Використовуйте 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);
  }
}

11
Це не уникне контрольних символів (наприклад, char 30).
zimdanen

19

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

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

Виведе

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

Якщо вам потрібно прочитати файл XML, який виправлений неправильно, не використовуйте регулярний вираз. Замість цього скористайтеся пакетом Html Agility .


Приємно. Чи є у вас еквівалентний метод для тих, хто використовує XmlElement?
djdanlib

3
Оновлення: Налаштування властивості InnerText XmlElement, схоже, уникає ситуацій. Відповів на моє власне запитання, хузза!
djdanlib

Отже, ваш xml неправильно сформований? подобається <Data>&</Data>?
Pierre-Alain Vigeant

2
Так, саме в цьому проблема.
Alireza Noori

2
Ви все ще можете отримати проблеми, якщо вміст ваших елементів містить недійсні символи, такі як зворотний простір (0x08), багато інших контрольних символів або сурогатні кодові точки.
Якубізон

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
// 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).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.