转义C#中的无效XML字符

Ali*_*ori 78 .net c# xml escaping

我有一个包含无效XML字符的字符串.在解析字符串之前,如何转义(或删除)无效的XML字符?

Igo*_*tov 101

作为删除无效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;
    }
}
Run Code Online (Sandbox Code Playgroud)

作为逃避无效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;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新: 应该提到的是,编码操作产生的字符串的长度大于或等于源字符串的长度.将编码字符串存储在具有长度限制的字符串列中的数据库中并在应用程序中验证源字符串长度以适应数据列限制时,这可能很重要.

  • 如果字符串用于 XML 值,请注意不要使用 XmlConvert.EncodeName。XML 名称限制比 XML 值限制更严格,名称编码会导致不必要的意外转义。 (3认同)
  • @Matt,不,它确实 - ["如果任何字符都不是有效的xml字符,则会抛出XmlException,其中包含遇到的第一个无效字符的信息."](http://msdn.microsoft.com/en-us /library/system.xml.xmlconvert.verifyxmlchars.aspx) (2认同)
  • @IgorKustov我的坏!返回值文档似乎与此相矛盾,谢谢你把我赶出去. (2认同)

BLU*_*IXY 61

使用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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不会转义控制字符(如char 30). (9认同)

Pie*_*ant 19

如果您正在编写xml,只需使用框架提供的类来创建xml.你不必为逃避或任何事情而烦恼.

Console.Write(new XElement("Data", "< > &"));
Run Code Online (Sandbox Code Playgroud)

会输出

<Data>&lt; &gt; &amp;</Data>
Run Code Online (Sandbox Code Playgroud)

如果需要读取格式错误的XML文件,请不要 使用正则表达式.相反,使用Html Agility Pack.

  • 更新:设置XmlElement的InnerText属性似乎正确地转义.回答我自己的问题,huzzah! (3认同)
  • 是的,这正是问题所在. (2认同)
  • 如果元素的内容包含无效字符(如退格键(0x08),许多其他控制字符或代理代码点),您仍会遇到问题. (2认同)

Urs*_*ili 9

这是上述方法 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();

}
Run Code Online (Sandbox Code Playgroud)

  • `?.` 是 `Null-Conditional Operator`。https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators (2认同)

Fra*_*s C 8

Irishman 提供的 RemoveInvalidXmlChars 方法不支持代理字符。要测试它,请使用以下示例:

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

    string newContent = RemoveInvalidXmlChars(content);

    Console.WriteLine(newContent);
}
Run Code Online (Sandbox Code Playgroud)

这将返回一个空字符串,但它不应该!它应该返回“\U00010330”,因为字符U+10330是一个有效的 XML 字符。

为了支持代理字符,我建议使用以下方法:

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();
}
Run Code Online (Sandbox Code Playgroud)