Mam ciąg zawierający nieprawidłowe znaki XML. Jak mogę zmienić znaczenie (lub usunąć) nieprawidłowe znaki XML przed przeanalizowaniem ciągu?
Mam ciąg zawierający nieprawidłowe znaki XML. Jak mogę zmienić znaczenie (lub usunąć) nieprawidłowe znaki XML przed przeanalizowaniem ciągu?
Odpowiedzi:
Jako sposób na usunięcie nieprawidłowych znaków XML sugeruję użycie metody XmlConvert.IsXmlChar . Został dodany od .NET Framework 4 i jest również prezentowany w Silverlight. Oto mała próbka:
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;
}
}
A jako sposób na uniknięcie nieprawidłowych znaków XML sugeruję użycie metody XmlConvert.EncodeName . Oto mała próbka:
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;
}
}
Aktualizacja: Należy wspomnieć, że operacja kodowania tworzy ciąg o długości większej lub równej długości łańcucha źródłowego. Może to być ważne, gdy przechowujesz zakodowany ciąg w bazie danych w kolumnie ciągów z ograniczeniem długości i sprawdzasz poprawność długości ciągu źródłowego w aplikacji w celu dopasowania do ograniczenia kolumny danych.
XmlConvert.VerifyXmlChars
nie zgłasza wyjątku, jeśli argument zawiera nieprawidłowe znaki, zwraca ciąg pusty (i zwraca argument, jeśli wszystkie zawarte znaki są prawidłowe). Spróbuj po prostu return XmlConvert.VerifyXmlChars (text) != null
.
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);
}
}
Jeśli piszesz XML, po prostu użyj klas dostarczonych przez framework, aby utworzyć XML. Nie będziesz musiał zawracać sobie głowy ucieczką ani niczym.
Console.Write(new XElement("Data", "< > &"));
Wyjdzie
<Data>< > &</Data>
Jeśli chcesz odczytać plik XML, który jest zniekształcony, nie używaj wyrażeń regularnych. Zamiast tego użyj pakietu Html Agility Pack .
<Data>&</Data>
?
Metoda RemoveInvalidXmlChars dostarczona przez Irishman nie obsługuje znaków zastępczych. Aby to przetestować, użyj następującego przykładu:
static void Main()
{
const string content = "\v\U00010330";
string newContent = RemoveInvalidXmlChars(content);
Console.WriteLine(newContent);
}
To zwraca pusty ciąg, ale nie powinno! Powinien zwrócić „\ U00010330”, ponieważ znak U + 10330 jest prawidłowym znakiem XML.
Aby wspierać znaki zastępcze, sugeruję użycie następującej metody:
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();
}
Oto zoptymalizowana wersja powyższej metody RemoveInvalidXmlChars, która nie tworzy nowej tablicy przy każdym wywołaniu, niepotrzebnie obciążając w ten sposób 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();
}
?.
składnia? w kolejce result?.Append(ch);
?
// Replace invalid characters with empty strings.
Regex.Replace(inputString, @"[^\w\.@-]", "");
Wzorzec wyrażenia regularnego [^ \ w. @ -] dopasowuje dowolny znak, który nie jest znakiem słowa, kropką, symbolem @ ani łącznikiem. Znak słowa to dowolna litera, cyfra dziesiętna lub łącznik interpunkcyjny, taki jak podkreślenie. Każdy znak, który pasuje do tego wzorca, jest zastępowany przez String.Empty, czyli ciąg zdefiniowany przez wzorzec zastępujący. Aby umożliwić wprowadzanie dodatkowych znaków przez użytkownika, dodaj te znaki do klasy znaków we wzorcu wyrażenia regularnego. Na przykład wzorzec wyrażenia regularnego [^ \ w. @ - \%] dopuszcza również symbol procentu i ukośnik odwrotny w ciągu wejściowym.
Regex.Replace(inputString, @"[!@#$%_]", "");
Odnieś się też do tego:
Usuwanie nieprawidłowych znaków z tagu nazwy XML - RegEx C #
Oto funkcja służąca do usuwania znaków z określonego ciągu 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);
}
Ta prosta metoda zastępuje nieprawidłowe znaki tą samą wartością, ale akceptowaną w kontekście XML.
Aby napisać ciąg, użyj XMLWriteStringWithoutIllegalCharacters (string UnfilteredString).
Aby odczytać ciąg, użyj XMLReadStringWithoutIllegalCharacters (string FilteredString).