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  world</Test>
这个行为文档隐藏在 WriteString 方法的文档中,但听起来它适用于整个类。
使用 Create 创建的 XmlWriter 的默认行为是抛出 尝试在 范围 0x-0x1F(不包括空格字符 0x9、0xA 和 0xD(。 这些无效的 XML 字符可以通过创建 XmlWriter 来写入 将"字符检查"属性设置为 false。这样做将导致 在替换为数字字符实体 (
�
的字符中 通过�x1F
(。此外,使用新的 运算符将用数字字符替换无效字符 默认情况下为实体。
因此,您似乎最终编写了无效字符,因为您使用的是 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> {
{ '&', "&"}, { '<', "<" }, { '>', ">" },
{ '"', """ }, { '''', "'" }
};
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
、维基百科和规范标记为有效(尽管不鼓励(字符