70

Xml 要素または属性で使用する文字列をエンコードするための .Net のジェネリック メソッドを探していましたが、すぐに見つからなかったので驚きました。では、先に進む前に、組み込み関数が不足している可能性はありますか?

本当に存在しないと仮定して、独自のジェネリックEncodeForXml(string data)メソッドを作成し、これを行うための最善の方法を考えています。

この全体を促した私が使用しているデータには、&、<、" などの不適切な文字が含まれている可能性があります。また、適切にエスケープされたエンティティ &、<、および " が含まれている場合もあります。 CDATA セクションは最良のアイデアではないかもしれません.それはちょっと不格好に思えます.xml で直接使用できる素敵な文字列値で終わるほうがずっとましです.

過去に正規表現を使用して悪いアンパサンドをキャッチしたことがありますが、この場合と最初のステップで正規表現を使用してキャッチし、他の文字を単純に置き換えることを考えています。

それで、これを複雑にしすぎずにさらに最適化することはできますか?私が見逃しているものはありますか? :

Function EncodeForXml(ByVal data As String) As String
    Static badAmpersand As new Regex("&(?![a-zA-Z]{2,6};|#[0-9]{2,4};)")

    data = badAmpersand.Replace(data, "&amp;")

    return data.Replace("<", "&lt;").Replace("""", "&quot;").Replace(">", "gt;")
End Function

C# のみの皆さん、申し訳ありませんが、どの言語を使用するかはあまり気にしませんが、Regex を静的にしたかったのですが、C# ではメソッドの外で宣言しないとそれを行うことができないため、これは VB になります。 。ネット

最後に、私はまだ私が働いている .Net 2.0 を使用していますが、誰かが最終製品を取得して、それを文字列クラスの拡張メソッドに変えることができれば、それもかなりクールです。

更新最初のいくつかの応答は、.Net には実際にこれを行う組み込みの方法があることを示しています。しかし、私が始めたので、EncodeForXml() メソッドを楽しみのために終わらせたいと思っているので、まだ改善のアイデアを探しています。特に: エンティティとしてエンコードする必要がある文字のより完全なリスト (おそらくリスト/マップに格納される) と、不変の文字列に対してシリアルで .Replace() を実行するよりも優れたパフォーマンスが得られるもの。

4

13 に答える 13

78

入力についての知識によっては、すべての Unicode 文字が有効な XML 文字であるとは限らないことを考慮する必要がある場合があります。

Server.HtmlEncodeSystem.Security.SecurityElement.Escapeはどちらも不正な XML 文字を無視するようですが、System.XML.XmlWriter.WriteStringは不正な文字に遭遇するとArgumentExceptionをスローします (そのチェックを無効にして無視しない限り)。ライブラリ関数の概要については、こちらを参照してください。

編集 2011/8/14:過去数年間で少なくとも数人がこの回答を参照したことを見て、私は元のコードを完全に書き直すことにしました。これには、UTF-16 のひどく誤った処理など、多くの問題がありました。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

/// <summary>
/// Encodes data so that it can be safely embedded as text in XML documents.
/// </summary>
public class XmlTextEncoder : TextReader {
    public static string Encode(string s) {
        using (var stream = new StringReader(s))
        using (var encoder = new XmlTextEncoder(stream)) {
            return encoder.ReadToEnd();
        }
    }

    /// <param name="source">The data to be encoded in UTF-16 format.</param>
    /// <param name="filterIllegalChars">It is illegal to encode certain
    /// characters in XML. If true, silently omit these characters from the
    /// output; if false, throw an error when encountered.</param>
    public XmlTextEncoder(TextReader source, bool filterIllegalChars=true) {
        _source = source;
        _filterIllegalChars = filterIllegalChars;
    }

    readonly Queue<char> _buf = new Queue<char>();
    readonly bool _filterIllegalChars;
    readonly TextReader _source;

    public override int Peek() {
        PopulateBuffer();
        if (_buf.Count == 0) return -1;
        return _buf.Peek();
    }

    public override int Read() {
        PopulateBuffer();
        if (_buf.Count == 0) return -1;
        return _buf.Dequeue();
    }

    void PopulateBuffer() {
        const int endSentinel = -1;
        while (_buf.Count == 0 && _source.Peek() != endSentinel) {
            // Strings in .NET are assumed to be UTF-16 encoded [1].
            var c = (char) _source.Read();
            if (Entities.ContainsKey(c)) {
                // Encode all entities defined in the XML spec [2].
                foreach (var i in Entities[c]) _buf.Enqueue(i);
            } else if (!(0x0 <= c && c <= 0x8) &&
                       !new[] { 0xB, 0xC }.Contains(c) &&
                       !(0xE <= c && c <= 0x1F) &&
                       !(0x7F <= c && c <= 0x84) &&
                       !(0x86 <= c && c <= 0x9F) &&
                       !(0xD800 <= c && c <= 0xDFFF) &&
                       !new[] { 0xFFFE, 0xFFFF }.Contains(c)) {
                // Allow if the Unicode codepoint is legal in XML [3].
                _buf.Enqueue(c);
            } else if (char.IsHighSurrogate(c) &&
                       _source.Peek() != endSentinel &&
                       char.IsLowSurrogate((char) _source.Peek())) {
                // Allow well-formed surrogate pairs [1].
                _buf.Enqueue(c);
                _buf.Enqueue((char) _source.Read());
            } else if (!_filterIllegalChars) {
                // Note that we cannot encode illegal characters as entity
                // references due to the "Legal Character" constraint of
                // XML [4]. Nor are they allowed in CDATA sections [5].
                throw new ArgumentException(
                    String.Format("Illegal character: '{0:X}'", (int) c));
            }
        }
    }

    static readonly Dictionary<char,string> Entities =
        new Dictionary<char,string> {
            { '"', "&quot;" }, { '&', "&amp;"}, { '\'', "&apos;" },
            { '<', "&lt;" }, { '>', "&gt;" },
        };

    // References:
    // [1] http://en.wikipedia.org/wiki/UTF-16/UCS-2
    // [2] http://www.w3.org/TR/xml11/#sec-predefined-ent
    // [3] http://www.w3.org/TR/xml11/#charsets
    // [4] http://www.w3.org/TR/xml11/#sec-references
    // [5] http://www.w3.org/TR/xml11/#sec-cdata-sect
}

単体テストと完全なコードは、ここにあります。

于 2009-04-08T22:27:03.947 に答える
33

SecurityElement.Escape

ここに文書化

于 2008-10-01T13:47:56.297 に答える
26

過去に、HttpUtility.HtmlEncode を使用して xml のテキストをエンコードしました。本当に同じタスクを実行します。まだ問題に遭遇したことはありませんが、今後問題が発生しないというわけではありません。名前が示すように、XML ではなく HTML 用に作成されました。

既にお読みになっていると思いますが、ここに xml のエンコードとデコードに関する記事があります。

編集: もちろん、xmlwriter または新しい XElement クラスの 1 つを使用する場合、このエンコーディングは自動的に行われます。実際、テキストを取得して新しい XElement インスタンスに配置し、要素の文字列 (.tostring) バージョンを返すことができます。SecurityElement.Escapeもユーティリティ メソッドと同じタスクを実行すると聞きましたが、それについてあまり読んだり、使用したりしていません。

EDIT2: あなたはまだ 2.0 を使用しているため、XElement に関する私のコメントは無視してください

于 2008-10-01T13:45:39.737 に答える
11

.net 3.5+

new XText("I <want> to & encode this for XML").ToString();

あなたにあげる:

I &lt;want&gt; to &amp; encode this for XML

このメソッドは、エンコードすべきもの (引用符など) をエンコードしていないことがわかりました。

SecurityElement.Escapeworkmad3の回答)はこれでより良い仕事をしているようで、以前のバージョンの.netに含まれています。

サードパーティのコードを気にせず、不正な文字が XML に含まれないようにしたい場合は、Michael Kropat の回答をお勧めします。

于 2012-02-22T01:31:04.620 に答える
5

XmlTextWriter.WriteString()エスケープを行います。

于 2008-10-01T13:48:26.623 に答える
4

System.XML がエンコーディングを処理するので、このようなメソッドは必要ありません。

于 2008-10-01T13:46:40.777 に答える
3

これは、WriteCData メソッドを使用することでメリットが得られる場合です。

public override void WriteCData(string text)
    Member of System.Xml.XmlTextWriter

Summary:
Writes out a <![CDATA[...]]> block containing the specified text.

Parameters:
text: Text to place inside the CDATA block.

簡単な例は次のようになります。

writer.WriteStartElement("name");
writer.WriteCData("<unsafe characters>");
writer.WriteFullEndElement();

結果は次のようになります。

<name><![CDATA[<unsafe characters>]]></name>

ノード値を読み取るとき、XMLReader は内部テキストの CData 部分を自動的に取り除くので、心配する必要はありません。唯一の問題は、データを innerText 値として XML ノードに格納する必要があることです。つまり、CData コンテンツを属性値に挿入することはできません。

于 2009-01-07T20:30:20.440 に答える
3

これが ASP.NET アプリの場合、 Server.HtmlEncode() を使用しないのはなぜですか?

于 2008-10-01T13:46:07.003 に答える
1

すべての無効な文字 (少数の「html」文字だけでなく) の処理に真剣に取り組んでおり、 にアクセスできる場合は、値データSystem.Xmlの適切な Xml エンコードを行う最も簡単な方法を次に示します。

string theTextToEscape = "Something \x1d else \x1D <script>alert('123');</script>";
var x = new XmlDocument();
x.LoadXml("<r/>"); // simple, empty root element
x.DocumentElement.InnerText = theTextToEscape; // put in raw string
string escapedText = x.DocumentElement.InnerXml; // Returns:  Something &#x1D; else &#x1D; &lt;script&gt;alert('123');&lt;/script&gt;

// Repeat the last 2 lines to escape additional strings.

XmlConvert.EncodeName()これは値ではなくエンティティ/タグ名のためであるため、適切ではないことを知っておくことが重要です。これを使用すると、Html エンコードが必要な場合の Url エンコードのようになります。

于 2018-03-19T16:32:58.833 に答える
0

エンコーディングを自動的に処理する組み込みクラスXAttributeを使用できます。

using System.Xml.Linq;

XDocument doc = new XDocument();

List<XAttribute> attributes = new List<XAttribute>();
attributes.Add(new XAttribute("key1", "val1&val11"));
attributes.Add(new XAttribute("key2", "val2"));

XElement elem = new XElement("test", attributes.ToArray());

doc.Add(elem);

string xmlStr = doc.ToString();
于 2015-04-23T11:04:48.150 に答える
0

これは、XElements を使用した単一行のソリューションです。私は非常に小さなツールでそれを使用します。二度と必要ないので、このままにしておきます。(その汚いダグ)

StrVal = (<x a=<%= StrVal %>>END</x>).ToString().Replace("<x a=""", "").Replace(">END</x>", "")

ああ、それはC#ではなくVBでのみ機能します

于 2017-03-30T09:55:02.430 に答える