XmlTextWriter错误地编写控制字符

时间:2011-11-24 11:03:41

标签: c# .net xml xmltextwriter control-characters

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

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

我有一个字符串,其中包含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>

3 个答案:

答案 0 :(得分:12)

此行为的文档隐藏在documentation of the WriteString method中,但听起来它适用于整个班级。

  

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

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分: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但是它也将删除DEL以及根据{{{U} 00 -F + U + 0084,U + 0086-U + 009F范围内的其他人维基百科上的3}}仅在某些上下文中有效,并且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剥离。