سؤال

كنت أبحث عن طريقة عامة في .NET لتشفير سلسلة للاستخدام في عنصر أو سمة XML ، وفوجئت عندما لم أجد واحدة على الفور. لذا ، قبل أن أذهب إلى أبعد من ذلك ، هل يمكنني فقط أن أفقد الوظيفة المدمجة؟

على افتراض لحظة أنه غير موجود حقًا ، فأنا أقوم بتجميع عامي العام 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, "&")

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

آسف على كل ما تبذلونه من الناس-لا يهمني حقًا اللغة التي أستخدمها ، لكنني أردت أن أجعل Regex ثابتًا ولا يمكنك فعل ذلك في C# دون إعلانها خارج الطريقة ، لذلك سيكون هذا VB .شبكة

أخيرًا ، ما زلنا على .NET 2.0 حيث أعمل ، ولكن إذا تمكن شخص ما من أخذ المنتج النهائي وتحويله إلى طريقة تمديد لفئة السلسلة ، فسيكون ذلك رائعًا أيضًا.

تحديث تشير الردود القليلة الأولى إلى أن .NET لديها بالفعل طرق مدمجة للقيام بذلك. لكن الآن بعد أن بدأت ، أريد نوعًا ما أن أنهي طريقة Encodeforxml () لمجرد المتعة ، لذلك ما زلت أبحث عن أفكار للتحسين. والجدير بالذكر: قائمة أكثر اكتمالا من الأحرف التي ينبغي تشفيرها ككيانات (ربما يتم تخزينها في قائمة/خريطة) ، وشيء يحصل على أداء أفضل من القيام بتراجع () على الأوتار غير القابلة للتسلية.

هل كانت مفيدة؟

المحلول

System.xml يتعامل مع الترميز لك ، لذلك لا تحتاج إلى طريقة كهذه.

نصائح أخرى

اعتمادًا على مقدار معرفتك بالمدخلات ، قد تضطر إلى مراعاة ذلك ليست جميع أحرف Unicode هي أحرف XML صالحة.

كلاهما server.htmlencode و System.Security.SecurityElement.escape يبدو أنه يتجاهل أحرف XML غير القانونية ، بينما System.xml.xmlWriter.WriteString يلقي engumentException عندما تواجه شخصيات غير قانونية (ما لم تقم بتعطيل ذلك ، فإنه يتجاهلها). تتوفر نظرة عامة على وظائف المكتبة هنا.

تحرير 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
}

يمكن العثور على اختبارات الوحدة والرمز الكامل هنا.

SecurityElement.escape

موثق هنا

في الماضي ، استخدمت httputily.htmlencode لتشفير النص لـ XML. ينفذ نفس المهمة ، حقا. لم أواجه أي مشاكل معها حتى الآن ، لكن هذا لا يعني أنني لن أفعل في المستقبل. كما يوحي الاسم ، تم صنعه لـ HTML ، وليس XML.

ربما تكون قد قرأته بالفعل ، لكن هنا مقال على XML تشفير وفك التشفير.

تحرير: بالطبع ، إذا كنت تستخدم XMLWriter أو أحد فئات XElement الجديدة ، فسيتم هذا الترميز من أجلك. في الواقع ، يمكنك فقط أخذ النص ، ووضعه في مثيل Xelement جديد ، ثم إرجاع إصدار السلسلة (.ToString) للعنصر. لقد سمعت أن SecurityElement.escape سيؤدي نفس المهمة مثل طريقة الأداة المساعدة الخاصة بك أيضًا ، ولكن لم يقرأها كثيرًا أو تستخدمها.

EDIT2: تجاهل تعليقي حول XElement ، نظرًا لأنك لا تزال على 2.0

Microsoft مكتبة Antixss antixssencoder فئة في system.web.dll لديه طرق لهذا:

AntiXss.XmlEncode(string s)
AntiXss.XmlAttributeEncode(string s)

لديها HTML كذلك:

AntiXss.HtmlEncode(string s)
AntiXss.HtmlAttributeEncode(string s)

في .NET 3.5+

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

يعطيك:

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

تبين أن هذه الطريقة لا تشفر بعض الأشياء التي ينبغي (مثل عروض الأسعار).

SecurityElement.Escape (إجابة WorkMad3) يبدو أنه يقوم بعمل أفضل مع هذا وهو مدرج في الإصدارات السابقة من .NET.

إذا كنت لا تمانع إجابة مايكل كروبات.

XmlTextWriter.WriteString() هل الهروب.

إذا كان هذا تطبيق ASP.NET ، فلماذا لا تستخدم server.htmlencode ()؟

قد يكون هذا هو الحال حيث يمكنك الاستفادة من استخدام طريقة 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 من النص الداخلي حتى لا تقلق بشأنه. الصيد الوحيد هو أنه يتعين عليك تخزين البيانات كقيمة نصية إلى عقدة XML. بمعنى آخر ، لا يمكنك إدراج محتوى CDATA في قيمة السمة.

متألق! هذا كل ما يمكنني قوله.

فيما يلي متغير VB للرمز المحدث (وليس في فئة ، مجرد وظيفة) من شأنه أن ينظف ويقوم أيضًا بتطهير XML

Function cXML(ByVal _buf As String) As String
    Dim textOut As New StringBuilder
    Dim c As Char
    If _buf.Trim Is Nothing OrElse _buf = String.Empty Then Return String.Empty
    For i As Integer = 0 To _buf.Length - 1
        c = _buf(i)
        If Entities.ContainsKey(c) Then
            textOut.Append(Entities.Item(c))
        ElseIf (AscW(c) = &H9 OrElse AscW(c) = &HA OrElse AscW(c) = &HD) OrElse ((AscW(c) >= &H20) AndAlso (AscW(c) <= &HD7FF)) _
            OrElse ((AscW(c) >= &HE000) AndAlso (AscW(c) <= &HFFFD)) OrElse ((AscW(c) >= &H10000) AndAlso (AscW(c) <= &H10FFFF)) Then
            textOut.Append(c)
        End If
    Next
    Return textOut.ToString

End Function

Shared ReadOnly Entities As New Dictionary(Of Char, String)() From {{""""c, "&quot;"}, {"&"c, "&amp;"}, {"'"c, "&apos;"}, {"<"c, "&lt;"}, {">"c, "&gt;"}}

يمكنك استخدام الفصل المدمج 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();

فيما يلي حل خط واحد باستخدام XElements. أستخدمه في أداة صغيرة جدًا. لا أحتاجها للمرة الثانية ، لذا احتفظ بها بهذه الطريقة. (ديردي دوغ)

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

أوه ، وهو يعمل فقط في VB ليس في C#

إذا كنت جادًا في التعامل مع الكل من الشخصيات غير الصالحة (وليس فقط القليلة "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() غير مناسب ، لأن هذا لأسماء الكيان/العلامات ، وليس القيم. باستخدام ذلك سيكون مثل ترميز URL عندما تحتاج إلى HTML-Encode.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top