我有一个xml文档,我正在尝试将节点彼此分离。我只希望根节点,然后只希望第二个节点,然后是第二个节点内存在的节点列表。我遇到了一个问题,当我从第二个节点或主节点中删除节点时,我的列表变为空。我不明白为什么会发生这种情况,尤其是由于以下这种奇怪的行为。
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.Load(@"C:\Users\Username\Desktop\Diagram.xml");
XmlNode rootNode = doc.DocumentElement;
XmlNode secondNode = doc.SelectSingleNode(rootNode.Name + "/root");
XmlNodeList nodelist = doc.SelectNodes("//root/mxCell");
Console.WriteLine("-----------------------------------------");
Console.WriteLine(RemoveChildren(rootNode).OuterXml);
Console.WriteLine("-----------------------------------------");
Console.WriteLine(RemoveChildren(secondNode).OuterXml);
Console.WriteLine("-----------------------------------------");
//Console.WriteLine(rootNode.OuterXml);
Console.WriteLine(nodelist.Count); //Becomes 0
if (nodelist != null && nodelist.Count > 0)
{
foreach (XmlNode n in nodelist)
{
Console.WriteLine(n.OuterXml);
}
}
Console.ReadLine();
}
private static XmlNode RemoveChildren(XmlNode n) {
while (n.FirstChild != null)
{
n.RemoveChild(n.FirstChild);
}
return n;
}
}
如果运行此代码,我的nodelist.count将变为0。为什么节点列表变为0,但是为什么我仍然可以访问第二个节点?
但是,如果我在doc.SelectNodes(“ // root / mxCell”)之后添加foreach循环;计数将变为4。
像这样,
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.Load(@"C:\Users\Username\Desktop\Diagram.xml");
XmlNode rootNode = doc.DocumentElement;
XmlNode secondNode = doc.SelectSingleNode(rootNode.Name + "/root");
XmlNodeList nodelist = doc.SelectNodes("//root/mxCell");
if (nodelist != null && nodelist.Count > 0)
{
foreach (XmlNode n in nodelist)
{
Console.WriteLine(n.OuterXml);
}
}
Console.WriteLine("-----------------------------------------");
Console.WriteLine(RemoveChildren(rootNode).OuterXml);
Console.WriteLine("-----------------------------------------");
Console.WriteLine(RemoveChildren(secondNode).OuterXml);
Console.WriteLine("-----------------------------------------");
//Console.WriteLine(rootNode.OuterXml);
Console.WriteLine(nodelist.Count); //Becomes 4
if (nodelist != null && nodelist.Count > 0)
{
foreach (XmlNode n in nodelist)
{
Console.WriteLine(n.OuterXml);
}
}
Console.ReadLine();
}
private static XmlNode RemoveChildren(XmlNode n) {
while (n.FirstChild != null)
{
n.RemoveChild(n.FirstChild);
}
return n;
}
}
现在计数为4。
这里是使用的xml:
<mxGraphModel dx="1086" dy="596" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="YJb7HCrh72y2aGPrfETQ-1" value="" style="endArrow=classic;html=1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="130" y="310" as="sourcePoint"/>
<mxPoint x="180" y="260" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="YJb7HCrh72y2aGPrfETQ-2" value="" style="endArrow=classic;html=1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="290" y="270" as="sourcePoint"/>
<mxPoint x="340" y="220" as="targetPoint"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
您正在观察的行为可以更简单地说明如下。以下单元测试将成功(演示小提琴here):
XmlNodeList nodelist = doc.SelectNodes("*/*"); // Select all children of the root node
RemoveChildren(doc.DocumentElement);
Assert.IsTrue(nodelist.Count == 0); // Passes
虽然以下操作将失败:
XmlNodeList nodelist = doc.SelectNodes("*/*"); // Select all children of the root node
Assert.IsTrue(nodelist.Count > 0); // Passes
RemoveChildren(doc.DocumentElement);
Assert.IsTrue(nodelist.Count == 0); // FAILS!?
为什么在删除某些节点之前仅添加nodelist.Count
会导致在删除节点后nodelist
的内容不一致?
事实证明,在这种情况下XmlNodeList
返回的SelectNodes()
没有指定的行为。来自documentation remarks for XmlNode.SelectNodes()
:
机制。一旦对节点列表进行计数或迭代(但不是在此之前),XPath查询将被评估一次,并且仅评估一次,并且结果已缓存并随后重用。)此方法返回的
XmlNode.SelectNodes()
对象将在基础文档保持不变的情况下有效。 如果基础文档发生更改,则可能会返回意外结果(不会引发异常)。>此类“意外结果”就是您正在观察的。一旦
XmlNodeList
被调用,.Net将不会指定或保证XmlNodeList
的内容。 (实际上,返回的RemoveChildren()
似乎使用lazy Evaluation
此XPath查询返回的节点列表的文档限制与nodelist
的常规文档相反,该文档指出:
对创建
XmlNodeList
集合的节点对象的子项的更改将立即反映在XmlNodeList
属性和方法返回的节点中。此注释似乎仅适用于
XmlNodeList
DOM对象的方法返回的XmlNodeList
子列表。