14

.NETXmlTextWriter会创建无效的 xml 文件。

在 XML 中,允许使用某些控制字符,例如“水平制表符”( 	),但不允许使用其他控制字符,例如“垂直制表符”( )。(见规格。)

我有一个字符串,其中包含 XML 中不允许的 UTF-8 控制字符。
尽管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>
4

3 回答 3

13

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

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

因此,您似乎最终编写了无效字符,因为您使用的是 XmlTextWriter 类。对您来说更好的解决方案是改用XmlWriter 类

于 2011-11-24T11:55:16.900 回答
3

当我在同一个问题上苦苦挣扎时发现了这个问题,最后我用正则表达式解决了这个问题:

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

希望它可以帮助某人作为替代解决方案。

于 2013-06-26T21:31:53.737 回答
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();
  • 如果设置CheckCharacterstrue(默认情况下)有点过于严格,因为它只会抛出异常,另一种对无效 XML 字符更宽松的替代方法是剥离它们:

谷歌搜索一下产生了白名单XmlTextEncoder但是它也会删除DEL和范围 U+007F–U+0084、U+0086–U+009F 中的其他内容,根据维基百科上的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 测试,除了期望它剥离DELwhich XmlConvert.IsXmlChar、 Wikipedia 和规范标记为有效(尽管不鼓励)字符的测试。

于 2011-11-24T21:37:38.690 回答