为什么LINQ to XML不会转义像'\x1A'这样的字符?

3

如果在XElement的内容中包含字符'\x1A'、'\x1B'、'\x1C'、'\x1D'、'\x1E'或'\x1F',会导致异常。

using System;
using System.Collections.Generic;
using System.Xml.Linq;

namespace LINQtoXMLInvalidChars
{
    class Program
    {
        private static readonly IReadOnlyCollection<char> InvalidCharactersInXml = new List<char>
        {
            '<',
            '>',
            '&',
            '\'',
            '\"',
            '\x1A',
            '\x1B',
            '\x1C',
            '\x1D',
            '\x1E',
            '\x1F'
        };

        static void Main()
        {
            foreach (var c in InvalidCharactersInXml)
            {
                var xEl = new XElement("tag", "Character: " + c);
                var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", null), xEl);

                try
                {
                    Console.Write("Writing " + c + ": ");
                    Console.WriteLine(xDoc);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oops.    " + e.Message);
                }
            }

            Console.ReadKey();
        }
    }
}

在Jon Skeet的回答《将字符串转义为XML》这个问题中,他说:

你设置节点中的文本,它会自动转义需要转义的任何内容。

所以我现在很困惑。我是否理解错了什么?

一些背景信息:XElement的字符串内容来自最终用户。我看到两个选项可以使我的应用程序更加健壮:1)在传递给XElement之前对字符串进行Base-64编码;2)将接受的字符集范围缩小到例如字母数字字符。

1个回答

4
大部分这些字符在XML 1.0中都是无效的。个人希望LINQ to XML不能生成随后无法解析的文档,但基本上你应该避免使用它们。我还建议尽量避免使用转义序列\x,而优先选择\u。因为\x最多可以使用4个十六进制数字,这一点可能会非常令人困惑。根据 XML 1.0规范

Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

现在U+000D和U+000A是有趣的情况——它们不会在文本节点中被转义;它们将直接以原样包含。是否在解析节点时存在取决于解析设置(以及周围是否有非空格字符)。至于如何在您的情况下处理它:您肯定有以下选择:
  • 自行进行编码/转义。这通常会有些痛苦,并且会导致与普通文档相比难以阅读的 XML 文档。您可以仅在需要时执行此操作,例如添加一个属性到元素中表示您已经这样做了。
  • 检测并删除 XML 中无效的字符。
  • 检测并拒绝包含 XML 中无效字符的字符串。

我们无法确定哪种方法在您的情况下最合适。


我希望我的序列化技术(在这种情况下为XML)对于我的组件的用户来说是“透明”的,因此我希望避免仅因输入包含XML中无效的字符而拒绝它们。不幸的是,删除字符对我来说不是一个选项。因此,我只能执行自己的编码/转义,但这似乎非常痛苦。目前,我正在使用这种方法来对字符串的字节表示进行Base64编码,但由于潜在的字节顺序问题,我还有所保留。 - Gyula Kósa
1
@GyulaKósa:将字符编码为字节的方法很糟糕。你至少应该使用UTF-8或类似的编码方式。你是否完全被强制使用XML?因为如果你不是真正将其用作文本表示,那么这种方法效率非常低下... - Jon Skeet
1
@GyulaKósa:不,它可以很好地编码U+D802——作为代理对的一部分。如果您有一个包含U+D802但没有相应低代理项的字符串,则该字符串根本不是格式良好的Unicode字符串。您真的需要考虑一下您要支持什么。序列化“任何格式良好的Unicode字符串”和“任何UTF-16代码单元序列”之间存在很大的区别。 - Jon Skeet
我认为我错误地假设了“一系列Unicode字符”(https://msdn.microsoft.com/en-us/library/system.string%28v=vs.110%29.aspx),即`String`对象始终是一个格式良好的Unicode字符串。 - Gyula Kósa
1
@Gyula:那个MSDN文档真是不幸。实际上,字符串是UTF-16码单元的序列。但是没错,这种方法应该是可以的 - 在元素中添加一个属性来指示它是base64编码的。 - Jon Skeet
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接