我有一堆与XmlDocument
的空格处理有关的问题。请参阅以下示例中的编号注释。
在混合模式下,所有空白都不应该显着吗?为什么a
标记之间的空格不重要?
虽然我知道实际的空白元素仍然是XmlWhitespace
,但我如何将这些空格规范化为XmlSignificantWhitespace
个节点? Normalize()
无效。
我是唯一可以手动选择的吗?
这是我的测试用例:
private static void Main()
{
// 1. Shouldn't all whitespace be significant in mixed mode? Why the space between the a tags is not significant?
var doc = new XmlDocument
{
InnerXml = "<root>test1 <a>test2</a> <a>test3</a></root>",
};
PrintDoc(doc);
// 2.a. While I understand that the actual whitespace element is still XmlWhitespace, how do I normalize these spaces into XmlSignificantWhitespaces?
doc.DocumentElement.RemoveAll();
doc.DocumentElement.SetAttribute("xml:space", "preserve");
var fragment = doc.CreateDocumentFragment();
fragment.InnerXml = "test1 <a>test2</a> <a>test3</a>";
doc.DocumentElement.PrependChild(fragment);
PrintDoc(doc);
// 2.b. Normalize doesn't work
doc.Normalize();
PrintDoc(doc);
// 3.a. Manual normalization does work, is there a better way?
doc.DocumentElement.RemoveAllAttributes();
var whitespaces = doc.DocumentElement.ChildNodes.Cast<XmlNode>()
.OfType<XmlWhitespace>()
.ToList();
foreach (var whitespace in whitespaces)
{
var significant = doc.CreateSignificantWhitespace(whitespace.Value);
doc.DocumentElement.ReplaceChild(significant, whitespace);
}
PrintDoc(doc);
// 3.b. Reading from string also works
doc.InnerXml = "<root xml:space=\"preserve\">test1 <a>test2</a> <a>test3</a></root>";
PrintDoc(doc);
}
private static void PrintDoc(XmlDocument doc)
{
var nodes = doc.DocumentElement.ChildNodes.Cast<XmlNode>().ToList();
var whitespace = nodes.OfType<XmlWhitespace>().Count();
var significantWhitespace = nodes.OfType<XmlSignificantWhitespace>().Count();
Console.WriteLine($"Xml: {doc.InnerXml}\nwhitespace: {whitespace}\nsignificant whitespace: {significantWhitespace}\n");
}
输出如下:
Xml: <root>test1 <a>test2</a><a>test3</a></root>
whitespace: 0
significant whitespace: 0
Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 1
significant whitespace: 0
Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 1
significant whitespace: 0
Xml: <root>test1 <a>test2</a> <a>test3</a></root>
whitespace: 0
significant whitespace: 1
Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 0
significant whitespace: 1
答案 0 :(得分:3)
Microsoft文档不清楚,至少部分不准确。虽然XmlSignificantWhitespace Class的Microsoft文档说“混合内容节点中的标记之间的空格” “重要的空白”,但实际的XmlDocument加载和解析行为与此不一致。相关文档为PreserveWhitespace和White Space and Significant White Space Handling when Loading the DOM,但这些文档未提供足够的具体细节。
根据经验,正如您通过测试用例和我自己的测试所证明的那样,行为如下:
XmlDocument.PreserveWhitespace = true
并且在xml:space="preserve"
范围内时,会保留空格。但是,对于前者,它保留在Whitespace
个节点而不是SignificantWhitespace
个节点中。XmlDocument.PreserveWhitespace = false
,则混合内容节点中的元素之间的空格被丢弃,相反到XmlSignificantWhitespace Class文档。SignfiicantWhitespace
范围内变为xml:space="preserve"
个节点。在这种情况下,无论SignificantWhitespace
设置如何,始终保留为XmlDocument.PreserveWhitespace
。简而言之,将空格直接解析为SignificantWhitespace
节点的唯一方法是在xml:space="preserve"
范围内。可能对您有用的一种方法是将XML内容包装在具有xml:space="preserve"
范围的新外部元素中。我不知道为什么你的CreateDocumentFragment()
测试不起作用,但这里有一些代码可以工作:
// 4. Loading the XML within an xml:space="preserve" element works
doc.InnerXml = "<root xml:space=\"preserve\"></root>";
doc.FirstChild.InnerXml = "test1 <a>test2</a> <a>test3</a>";
PrintDoc(doc);
这导致:
Xml: <root xml:space="preserve">test1 <a>test2</a> <a>test3</a></root>
whitespace: 0
significant whitespace: 1
答案 1 :(得分:1)
编写自己的XmlNodeReader
似乎有效,尽管它不是&#34;最干净的&#34;溶液
考虑当前的实施here:
public virtual XmlNodeType MoveToContent() {
do {
switch (this.NodeType) {
case XmlNodeType.Attribute:
MoveToElement();
goto case XmlNodeType.Element;
case XmlNodeType.Element:
case XmlNodeType.EndElement:
case XmlNodeType.CDATA:
case XmlNodeType.Text:
case XmlNodeType.EntityReference:
case XmlNodeType.EndEntity:
return this.NodeType;
}
} while (Read());
return this.NodeType;
}
要将标记SignificantWhitespace
作为内容,您可以在NodeType
时返回XmlNodeType.SignificantWhitespace
。
这是我自己WhitespaceXmlNodeReader
的完整实现:
internal class WhitespaceXmlNodeReader : XmlNodeReader
{
public WhitespaceXmlNodeReader(XmlNode node)
: base(node)
{
}
public override XmlNodeType MoveToContent()
{
do
{
switch (NodeType)
{
case XmlNodeType.Attribute:
MoveToElement();
goto case XmlNodeType.Element;
case XmlNodeType.Element:
case XmlNodeType.EndElement:
case XmlNodeType.CDATA:
case XmlNodeType.Text:
case XmlNodeType.EntityReference:
case XmlNodeType.EndEntity:
// This was added:
case XmlNodeType.SignificantWhitespace:
return NodeType;
}
} while (Read());
return NodeType;
}
}