【问题标题】:Escape invalid XML characters in C#在 C# 中转义无效的 XML 字符
【发布时间】:2012-01-09 23:51:55
【问题描述】:

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

【问题讨论】:

  • 您能提供更多上下文吗?样本输入和样本预期输出。还有你打算如何处理输出。
  • 您在编写 XML 吗?还是您尝试读取实际上不是 XML 的 XML?
  • 使用 XmlWriter,它会为你转义无效字符
  • @alireza 如果您在 cmets 中回答人们向您提出的问题(了解更多信息),您将获得更多有用的答案...
  • 对不起。我离开了几个小时。请阅读导致此问题的问题:stackoverflow.com/questions/8330619/… 您将在那里获得所需的所有信息

标签: c# .net xml escaping


【解决方案1】:

作为删除无效 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 如果参数包含无效字符,则不会抛出异常,它返回空字符串(如果所有包含的字符都有效,则返回参数)。试试return XmlConvert.VerifyXmlChars (text) != null
  • @IgorKustov 我的错!返回值文档似乎与此相矛盾,感谢您发现我。
  • 如果字符串用于 XML 值,请注意不要使用 XmlConvert.EncodeName。 XML 名称限制比 XML 值限制更严格,名称编码会导致不必要的意外转义。
  • @arik 我的代码仅用于演示目的,以显示转换前后 XML 字符串的状态。显然,在你的代码中你不需要验证它。
【解决方案2】:

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

【讨论】:

  • 这不会转义控制字符(如 char 30)。
【解决方案3】:

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

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

会输出

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

如果您需要读取格式错误的 XML 文件,不要 use 正则表达式。请改用Html Agility Pack

【讨论】:

  • 不错。对于使用 XmlElement 的人,您有等效的方法吗?
  • 更新:设置 XmlElement 的 InnerText 属性似乎可以正确转义。回答了我自己的问题,huzzah!
  • 所以你的 xml 格式错误?喜欢&lt;Data&gt;&amp;&lt;/Data&gt;
  • 是的,这正是问题所在。
  • 如果您的元素内容包含无效字符,如退格 (0x08)、许多其他控制字符或代理代码点,您仍然会遇到问题。
【解决方案4】:

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

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

    string newContent = RemoveInvalidXmlChars(content);

    Console.WriteLine(newContent);
}

这会返回一个空字符串,但它不应该!它应该返回“\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();
}

【讨论】:

    【解决方案5】:

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

    【讨论】:

    【解决方案6】:
    // Replace invalid characters with empty strings.
       Regex.Replace(inputString, @"[^\w\.@-]", ""); 
    

    正则表达式模式 [^\w.@-] 匹配任何不是单词字符、句点、@ 符号或连字符的字符。单词字符是任何字母、十进制数字或标点符号连接符,例如下划线。与此模式匹配的任何字符都将替换为 String.Empty,这是由替换模式定义的字符串。要允许用户输入中的其他字符,请将这些字符添加到正则表达式模式中的字符类中。例如,正则表达式模式 [^\w.@-\%] 还允许在输入字符串中使用百分比符号和反斜杠。

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

    也参考这个:

    Removing Invalid Characters from XML Name Tag - RegEx C#

    这是一个从指定的 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;
            }
        }
    }
    

    【讨论】:

      【解决方案7】:
      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)。

      【讨论】:

        【解决方案8】:

        如果您只是为在 XML 标记内使用的字符串转义无效的 XML 字符,您可以执行类似这样的简单操作。

        这在您不使用 XML 库时有效。

        public string EscapeXMLCharacters (string target)
        {
            return
                target
                    .Replace("&", "&amp;")
                    .Replace("<", "&lt;")
                    .Replace(">", "&gt;")
                    .Replace("\"", "&quot;")
                    .Replace("'", "&apos;");
        }
        

        你可以这样称呼它:

        public string GetXMLBody(string content)
        {
            return @"<input>" + EscapeXMLCharacters(content) + "</input>";
        }
        

        【讨论】:

          猜你喜欢
          • 2021-10-31
          • 2013-09-13
          • 2011-08-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多