У мене є рядок, що містить недійсні символи XML. Як я можу уникнути (або видалити) недійсні символи XML, перш ніж проаналізувати рядок?
У мене є рядок, що містить недійсні символи XML. Як я можу уникнути (або видалити) недійсні символи XML, перш ніж проаналізувати рядок?
Відповіді:
Як спосіб видалення недійсних символів 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
.
Використовуйте SecurityElement.Escape
using System;
using System.Security;
class Sample {
static void Main() {
string text = "Escape characters : < > & \" \'";
string xmlText = SecurityElement.Escape(text);
//output:
//Escape characters : < > & " '
Console.WriteLine(xmlText);
}
}
Якщо ви пишете xml, просто використовуйте класи, передбачені фреймворком, для створення xml. Вам не доведеться заморочуватися втечею або чим-небудь.
Console.Write(new XElement("Data", "< > &"));
Виведе
<Data>< > &</Data>
Якщо вам потрібно прочитати файл XML, який виправлений неправильно, не використовуйте регулярний вираз. Замість цього скористайтеся пакетом Html Agility .
<Data>&</Data>
?
Метод 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 без потреби:
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);
?
// 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;
}
}
}
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).