Thoát các ký tự XML không hợp lệ trong C #


83

Tôi có một chuỗi chứa các ký tự XML không hợp lệ. Làm cách nào để thoát (hoặc xóa) các ký tự XML không hợp lệ trước khi tôi phân tích cú pháp chuỗi?


2
Bạn có thể cung cấp thêm ngữ cảnh? Một đầu vào mẫu và một đầu ra dự kiến ​​mẫu. Ngoài ra, bạn định làm gì với đầu ra.
Darin Dimitrov

5
Bạn có đang viết XML không? Hay bạn đang cố đọc XML mà thực sự không phải là XML?
Marc Gravell

3
Sử dụng một XmlWriter, nó sẽ thoát khỏi nhân vật không hợp lệ cho bạn
Thomas Levesque

2
@alireza, bạn sẽ nhận được nhiều câu trả lời hữu ích hơn nếu bạn trả lời các câu hỏi mà mọi người đang hỏi bạn (để biết thêm thông tin) ở đây trong phần nhận xét ...
Marc Gravell

Tôi xin lỗi. Tôi đã đi một vài giờ. Xin vui lòng đọc các câu hỏi đó dẫn đến một này: stackoverflow.com/questions/8330619/... Bạn sẽ nhận được tất cả các thông tin mà bạn cần có
Alireza Noori

Câu trả lời:


112

Để loại bỏ các ký tự XML không hợp lệ, tôi khuyên bạn nên sử dụng phương thức XmlConvert.IsXmlChar . Nó đã được thêm vào từ .NET Framework 4 và cũng được trình bày trong Silverlight. Đây là mẫu nhỏ:

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

Và là cách để thoát các ký tự XML không hợp lệ, tôi khuyên bạn nên sử dụng phương thức XmlConvert.EncodeName . Đây là mẫu nhỏ:

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

Cập nhật: Cần lưu ý rằng hoạt động mã hóa tạo ra một chuỗi có độ dài lớn hơn hoặc bằng độ dài của chuỗi nguồn. Điều này có thể quan trọng khi bạn lưu trữ một chuỗi được mã hóa trong cơ sở dữ liệu trong cột chuỗi có giới hạn độ dài và xác thực độ dài chuỗi nguồn trong ứng dụng của bạn để phù hợp với giới hạn cột dữ liệu.


XmlConvert.VerifyXmlCharskhông ném ra ngoại lệ nếu đối số chứa các ký tự không hợp lệ, nó trả về chuỗi null (và trả về đối số nếu tất cả các ký tự được chứa đều hợp lệ). Hãy thử chỉ return XmlConvert.VerifyXmlChars (text) != null.
Matt Enright


3
@IgorKustov Cái xấu của tôi! Tài liệu về giá trị trả lại có vẻ mâu thuẫn với điều đó, cảm ơn vì đã giúp tôi hiểu rõ.
Matt Enright

3
Cẩn thận không sử dụng XmlConvert.EncodeName nếu chuỗi dành cho giá trị XML. Các hạn chế tên XML chặt chẽ hơn thì các hạn chế giá trị XML và mã hóa tên sẽ dẫn đến việc thoát không mong muốn không cần thiết.
David Burg

1
@arik mã của tôi chỉ phục vụ mục đích thể hiện, để hiển thị trạng thái của một chuỗi XML trước và sau khi chuyển đổi. Rõ ràng, trong mã của bạn, bạn không cần xác thực nó.
Igor Kustov 19/02/18

66

Sử dụng 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
Điều này không thoát khỏi các ký tự điều khiển (như ký tự 30).
zimdanen

19

Nếu bạn đang viết xml, chỉ cần sử dụng các lớp được cung cấp bởi khung công tác để tạo xml. Bạn sẽ không phải bận tâm đến việc trốn thoát hay bất cứ thứ gì.

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

Sẽ xuất

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

Nếu bạn cần đọc tệp XML không đúng định dạng, không sử dụng biểu thức chính quy. Thay vào đó, hãy sử dụng Gói Nhanh nhẹn Html .


Đẹp. Bạn có phương pháp tương đương cho ai đó sử dụng XmlElement không?
djdanlib

3
Cập nhật: Đặt thuộc tính InnerText của XmlElement dường như để thoát mọi thứ một cách chính xác. Đã trả lời câu hỏi của riêng tôi, huzzah!
djdanlib

Vì vậy, xml của bạn được hình thành mal? như thế <Data>&</Data>nào?
Lễ hội Pierre-Alain

2
Vâng, đó chính xác là vấn đề.
Alireza Noori

2
Bạn vẫn có thể gặp sự cố nếu nội dung các phần tử của bạn chứa các ký tự không hợp lệ như dấu cách lùi (0x08), nhiều ký tự điều khiển khác hoặc các điểm mã thay thế.
jakubiszon

6

Phương thức RemoveInvalidXmlChars do Irishman cung cấp không hỗ trợ các ký tự thay thế. Để kiểm tra nó, hãy sử dụng ví dụ sau:

static void Main()
{
    const string content = "\v\U00010330";

    string newContent = RemoveInvalidXmlChars(content);

    Console.WriteLine(newContent);
}

Điều này trả về một chuỗi trống nhưng nó không nên! Nó phải trả về "\ U00010330" vì ký tự U + 10330 là một ký tự XML hợp lệ.

Để hỗ trợ các ký tự thay thế, tôi khuyên bạn nên sử dụng phương pháp sau:

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

Đây là phiên bản được tối ưu hóa của phương thức RemoveInvalidXmlChars ở trên, phương thức này không tạo một mảng mới trong mỗi lần gọi, do đó nhấn mạnh GC một cách không cần thiết:

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

}

?.Cú pháp này là gì? trong hàng result?.Append(ch);?
JB. Với Monica.


1
// Replace invalid characters with empty strings.
   Regex.Replace(inputString, @"[^\w\.@-]", ""); 

Mẫu biểu thức chính quy [^ \ w. @ -] khớp với bất kỳ ký tự nào không phải là ký tự từ, dấu chấm, ký hiệu @ hoặc dấu gạch ngang. Một ký tự từ là bất kỳ ký tự nào, chữ số thập phân hoặc kết nối dấu câu, chẳng hạn như dấu gạch dưới. Bất kỳ ký tự nào phù hợp với mẫu này sẽ được thay thế bằng String.Empty, là chuỗi được xác định bởi mẫu thay thế. Để cho phép các ký tự bổ sung trong dữ liệu nhập của người dùng, hãy thêm các ký tự đó vào lớp ký tự trong mẫu biểu thức chính quy. Ví dụ: mẫu biểu thức chính quy [^ \ w. @ - \%] cũng cho phép ký hiệu tỷ lệ phần trăm và dấu gạch chéo ngược trong chuỗi nhập.

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

Tham khảo thêm cái này:

Xóa các ký tự không hợp lệ khỏi thẻ tên XML - RegEx C #

Đây là một hàm để xóa các ký tự khỏi một chuỗi XML được chỉ định:

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

Phương pháp đơn giản này thay thế các ký tự không hợp lệ bằng cùng một giá trị nhưng được chấp nhận trong ngữ cảnh XML.


Để viết chuỗi, hãy sử dụng XMLWriteStringWithoutIllegalCharacters (chuỗi UnfilteredString).
Để đọc chuỗi, hãy sử dụng XMLReadStringWithoutIllegalCharacters (chuỗi FilteredString).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.