XmlTextWriter 错误地写入控制字符

本文关键字:控制字符 错误 XmlTextWriter | 更新日期: 2023-09-27 18:15:32

.NET 的XmlTextWriter创建无效的 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>

XmlTextWriter 错误地写入控制字符

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

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

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

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

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

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

内置的 .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和其他字符,根据维基百科上的有效XML字符仅在某些情况下有效,并且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、维基百科和规范标记为有效(尽管不鼓励(字符

的测试。