XmlTextWriter错误地写入控制字符。

15

.NET的XmlTextWriter创建了无效的xml文件。

在XML中,有些控制字符是允许的,比如“水平制表符”(	),但有些是不允许的,比如“垂直制表符”()。 (参见规范。)

我有一个包含UTF-8控制字符的字符串,该字符在XML中不被允许。
虽然XmlTextWriter会转义该字符,但生成的XML文件仍然是无效的。

我应该如何确保XmlTextWriter永远不会生成非法的XML文件?

或者,如果使用XmlTextWriter无法实现此目的,我应该如何从字符串中删除在XML中不允许的特定控制字符?

示例代码:

using (XmlTextWriter writer =
  new XmlTextWriter("test.xml", Encoding.UTF8))
{
  writer.WriteStartDocument();
  writer.WriteStartElement("Test");
  writer.WriteValue("hello \xb world");
  writer.WriteEndElement();
  writer.WriteEndDocument();
}

输出:

<?xml version="1.0" encoding="utf-8"?><Test>hello &#xB; world</Test>

XML中不能有转义的垂直制表符吗?你能参考一下标准吗? - Jodrell
@Jodrell 没错,你不能这样做。XML 适用于文本,而不是控制字符或二进制数据。http://www.w3.org/TR/REC-xml/#charsets - jasso
3个回答

14

这个行为的文档被隐藏在WriteString方法文档中,但听起来好像适用于整个类。

使用Create创建的XmlWriter的默认行为是,在尝试写入0x-0x1F范围内的字符值(不包括空格字符0x9、0xA和0xD)时抛出ArgumentException异常。可以通过使用CheckCharacters属性设置为false创建XmlWriter来写入这些无效的XML字符。这样做会将字符替换为数值字符实体(&#0;&#0x1F)。此外,使用new运算符创建的XmlTextWriter默认情况下也会将无效字符替换为数值字符实体。

因此,似乎您之所以会写入无效的字符,是因为您正在使用XmlTextWriter类。更好的解决方案是改用XmlWriter类


有点奇怪,但显然即使存在 XmlTextWriter 构造函数,也不应该使用它:http://msdn.microsoft.com/en-us/library/kkz7cs0d.aspx - Tom Lokhorst

5

当我遇到同样的问题时,发现了这个问题,并最终通过正则表达式解决了它:

return Regex.Replace(s, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", "");

希望这个作为另一种解决方案能对某些人有所帮助。


1

在 .NET 中内置的转义器,如 SecurityElement.Escape 也不能很好地转义/剥离它。

  • 如果您的应用程序是唯一与文件交互的,则可以在编写器和读取器上同时将 CheckCharacters 设置为 false。生成的 XML 文件仍然是技术上无效的。

参见:

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = new UTF8Encoding(false);
xmlWriterSettings.CheckCharacters = false;
var sb = new StringBuilder();
var w = XmlWriter.Create(sb, xmlWriterSettings);
w.WriteStartDocument();
w.WriteStartElement("Test");
w.WriteString("hello \xb world");
w.WriteEndElement();
w.WriteEndDocument();
w.Close();
var xml = sb.ToString();
  • 如果将CheckCharacters设置为true(默认情况下是这样),如果过于严格,它只会简单地抛出异常,一个更宽容于无效XML字符的替代方法是将它们剥离:

通过谷歌一下找到了白名单XmlTextEncoder,然而它也会删除范围为U+007F–U+0084、U+0086–U+009F的DEL和其他字符,根据维基百科上的Valid XML Characters,这些字符在某些上下文中是有效的,虽然RFC提到它们已经不被推荐使用,但仍然是有效字符。

public static class XmlTextExtentions
{
    private static readonly Dictionary<char, string> textEntities = new Dictionary<char, string> {
        { '&', "&amp;"}, { '<', "&lt;" }, { '>', "&gt;" }, 
        { '"', "&quot;" }, { '\'', "&apos;" }
    };
    public static string ToValidXmlString(this string str)
    {
        var stripped = str
            .Select((c,i) => new 
            { 
                c1 = c, 
                c2 = i + 1 < str.Length ? str[i+1]: default(char),
                v = XmlConvert.IsXmlChar(c),
                p = i + 1 < str.Length ? XmlConvert.IsXmlSurrogatePair(str[i + 1], c) : false,
                pp = i > 0 ? XmlConvert.IsXmlSurrogatePair(c, str[i - 1]) : false
            })
            .Aggregate("", (s, c) => {                  
                if (c.pp)
                    return s;
                if (textEntities.ContainsKey(c.c1))
                    s += textEntities[c.c1];
                else if (c.v)
                    s += c.c1.ToString();
                else if (c.p)
                    s += c.c1.ToString() + c.c2.ToString();
                return s;
            });
        return stripped;
    }
}

这个程序通过了所有的XmlTextEncoder测试,除了一个期望它剥离DEL的测试,而XmlConvert.IsXmlChar、维基百科和规范都将其标记为有效(尽管不建议使用)字符。


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