6

我似乎发现 .Net 3.5 中的各种 XML 实现之间存在一些不一致之处,我正在努力找出名义上正确的方法。

这个问题实际上很容易重现:

  1. 使用包含 '\t' 字符的文本元素创建一个简单的 xml 文档,并为其赋予包含 '\t' 字符的属性:

    var xmlDoc = new XmlDocument { PreserveWhitespace = false, };
    xmlDoc.LoadXml("<test><text attrib=\"Tab'\t'space' '\">Tab'\t'space' '</text></test>");
    xmlDoc.Save(@"d:\TabTest.xml");
    

    注意:这意味着 XmlDocument 本身对属性值中的 '\t' 字符非常满意。

  2. 使用新的 XmlTextReader 加载文档:

    var rawFile = XmlReader.Create(@"D:\TabTest.xml");
    var rawDoc = new XmlDocument();
    rawDoc.Load(rawFile);
    
  3. 使用 XmlReader.Create 加载文档:

    var rawFile2 = new XmlTextReader(@"D:\TabTest.xml");
    var rawDoc2 = new XmlDocument();
    rawDoc2.Load(rawFile2);
    
  4. 比较调试器中的文档:

    (rawDoc).InnerXml   "<test><text attrib=\"Tab' 'space' '\">Tab'\t'space' '</text></test>"   string
    (rawDoc2).InnerXml  "<test><text attrib=\"Tab'\t'space' '\">Tab'\t'space' '</text></test>"  string
    

使用 new XmlTextReader 读取的文档是我所期望的,文本值和属性值中的 '\t' 都按预期存在。但是,如果您查看 XmlReader.Create 读取的文档,您会发现属性值中的 '\t' 字符将已转换为' '字符。

什么……!!:-)

经过一番谷歌搜索后,我发现我可以将 '\t' 编码为 ' ' - 如果我在示例 XML 中使用它而不是 '\t' ,则两个阅读器都按预期工作。

现在 Altova XmlSpy 和其他各种 XML 阅读器似乎对属性值中的 '\t' 字符非常满意,我的问题是处理这个问题的正确方法是什么?

我是否应该使用以 XmlReader.Create 等属性值编码的 '\t' 字符编写 XML 文件,或者其他 XML 工具是否正确且 '\t' 字符有效且 XmlReader.Create 已损坏?

我应该以哪种方式修复/解决此问题?

4

4 回答 4

2

可能与属性值规范化有关。对于 CDATA 属性,需要 XML 解析器将属性值中的换行符和制表符替换为空格,除非它们以转义形式编写为字符引用。

于 2012-09-04T14:18:53.790 回答
2

@all:感谢您的所有回答和评论。

Justin 和 Michael Kay 似乎是正确的,应该根据 W3C XML 规范对空格进行编码,而问题是大量的 MS 实现不满足这一要求。

就我而言,除了 XML 规范之外,我真正想要的只是正确保存属性值——即保存的值应该正是读取的值。

答案是在保存 XML 文件时强制使用通过使用 XmlWriter.Create 方法创建的 XmlWriter。

虽然 Dataset 和 XmlDocument 都提供了保存/写入机制,但在以默认形式使用时,它们都不能正确编码属性中的空格。但是,如果我强制他们使用手动创建的 XmlWriter,则会应用正确的编码并将其写入文件。

于是原来的文件保存代码就变成了:

var xmlDoc = new XmlDocument { PreserveWhitespace = false, };
xmlDoc.LoadXml("<test><text attrib=\"Tab'\t'space' '\">Tab'\t'space' '</text></test>");

using (var xmlWriter = XmlWriter.Create(@"d:\TabTest.Encoded.xml"))
{
    xmlDoc.Save(xmlWriter);
}

然后,此编写器以对称方式正确编码空白空间,以供 XmlReader.Create 读取器读取,而无需更改属性值。

这里要注意的另一件事是,该解决方案完全封装了我的代码中的编码,因为读写器在读写时透明地执行编码和解码。

于 2012-09-05T09:33:58.680 回答
1

乍一看似乎XmlTextReader符合W3C 建议。参见推荐中关于属性值归一化的部分,特别是

对于空格字符(#x20、#xD、#xA、#x9),将空格字符(#x20)附加到标准化值。

因此,您没有预料到的行为(看到空格而不是选项卡)实际上是正确的推荐行为。

我不知道为什么XmlTextReader会这样(文档中没有任何内容),但是您似乎已经确定了正确的解决方法 - 将属性编码为&#x9;。在这种情况下,规范化的字符串将包含制表符本身。

于 2012-09-04T15:12:49.127 回答
1

查看XmlReaderSettings.ComformanceLevel。特别是,这个描述:

请注意,默认情况下,由 Create 方法创建的 XmlReader 对象比 XmlTextReader 类更符合要求。以下是未在 XmlTextReader 上启用的一致性改进,但默认情况下可用于由 Create 方法创建的阅读器

于 2012-09-04T15:18:45.050 回答