無効な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
引数に無効な文字が含まれている場合は例外をスローせず、null文字列を返します(含まれているすべての文字が有効な場合は引数を返します)。試してみてください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);
}
}
xmlを作成している場合は、フレームワークが提供するクラスを使用してxmlを作成します。エスケープなどを気にする必要はありません。
Console.Write(new XElement("Data", "< > &"));
出力します
<Data>< > &</Data>
不正な形式のXMLファイルを読み取る必要がある場合は、正規表現を使用しない でください。代わりに、Html AgilityPackを使用してください。
<Data>&</Data>
?
Irishmanが提供するRemoveInvalidXmlCharsメソッドは、代理文字をサポートしていません。それをテストするには、次の例を使用します。
static void Main()
{
const string content = "\v\U00010330";
string newContent = RemoveInvalidXmlChars(content);
Console.WriteLine(newContent);
}
これは空の文字列を返しますが、そうすべきではありません!文字U + 10330は有効なXML文字であるため、「\ U00010330」を返す必要があります。
代理文字をサポートするには、次の方法を使用することをお勧めします。
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。@-]は、単語文字、ピリオド、@記号、またはハイフン以外のすべての文字に一致します。単語文字は、任意の文字、10進数、またはアンダースコアなどの句読点コネクタです。このパターンに一致する文字はすべて、置換パターンによって定義された文字列であるString.Emptyに置き換えられます。ユーザー入力で追加の文字を許可するには、それらの文字を正規表現パターンの文字クラスに追加します。たとえば、正規表現パターン[^ \ w。@-\%]では、入力文字列にパーセント記号と円記号を使用することもできます。
Regex.Replace(inputString, @"[!@#$%_]", "");
これも参照してください:
指定した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(string UnfilteredString)を使用します。
文字列を読み取るには、XMLReadStringWithoutIllegalCharacters(string FilteredString)を使用します。