C#で無効なXML文字をエスケープする


83

無効なXML文字を含む文字列があります。文字列を解析する前に、無効なXML文字をエスケープ(または削除)するにはどうすればよいですか?


2
より多くのコンテキストを提供できますか?サンプル入力とサンプル期待出力。また、出力をどうするつもりですか。
ダリンディミトロフ

5
XMLを書いていますか?それとも、実際にはXMLではないXMLを読み込もうとしていますか?
MarcGravell

3
XmlWriterを使用すると、無効な文字がエスケープされます
Thomas Levesque 2011年

2
@alirezaコメントで人々が(詳細について)尋ねている質問に答えると、より有用な答えが得られます...
MarcGravell

申し訳ありません。私は数時間不在でした。これにつながった質問を読んでください:stackoverflow.com/questions/8330619/…あなたはそこで必要なすべての情報を得るでしょう
Alireza Noori 2011年

回答:


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引数に無効な文字が含まれている場合は例外をスローせず、null文字列を返します(含まれているすべての文字が有効な場合は引数を返します)。試してみてくださいreturn XmlConvert.VerifyXmlChars (text) != null
Matt Enright 2013


3
@IgorKustov私の悪い!戻り値のドキュメントは、私を捕まえてくれてありがとう、それと矛盾しているようです。
Matt Enright 2013年

3
文字列がXML値を対象としている場合は、XmlConvert.EncodeNameを使用しないように注意してください。XML名の制限は、XML値の制限よりも厳しく、名前のエンコードにより、予期しない不要なエスケープが発生します。
デヴィッド・ブルク

1
@arik私のコードは、変換の前後のXML文字列の状態を表示するという目的を示すためだけに機能します。明らかに、コードでは検証する必要はありません。
Igor Kustov 2018

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 2013

19

xmlを作成している場合は、フレームワークが提供するクラスを使用してxmlを作成します。エスケープなどを気にする必要はありません。

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

出力します

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

不正な形式のXMLファイルを読み取る必要がある場合は、正規表現を使用しないください。代わりに、Html AgilityPackを使用してください。


いいね。XmlElementを使用している人と同等の方法はありますか?
djdanlib 2011年

3
更新:XmlElementのInnerTextプロパティを設定すると、正しくエスケープされているように見えます。私自身の質問に答えました、万歳!
djdanlib 2011年

それであなたのxmlは不正な形式ですか?のように<Data>&</Data>
Pierre-Alain Vigeant 2011

2
はい、それはまさに問題です。
Alireza Noori 2011

2
要素のコンテンツにバックスペース(0x08)、他の多くの制御文字、代理コードポイントなどの無効な文字が含まれている場合でも、問題が発生する可能性があります。
jakubiszon 2014年

6

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

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。@-]は、単語文字、ピリオド、@記号、またはハイフン以外のすべての文字に一致します。単語文字は、任意の文字、10進数、またはアンダースコアなどの句読点コネクタです。このパターンに一致する文字はすべて、置換パターンによって定義された文字列であるString.Emptyに置き換えられます。ユーザー入力で追加の文字を許可するには、それらの文字を正規表現パターンの文字クラスに追加します。たとえば、正規表現パターン[^ \ w。@-\%]では、入力文字列にパーセント記号と円記号を使用することもできます。

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

これも参照してください:

XML名タグからの無効な文字の削除-RegExC#

指定した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(string UnfilteredString)を使用します。
文字列を読み取るには、XMLReadStringWithoutIllegalCharacters(string FilteredString)を使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.