如何使用LINQ在未知的子元素位置选择子节点

时间:2012-10-21 19:44:51

标签: c# xml linq xpath

我的XML文件包含类似于以下内容的结构:

<root>
    <Manager name="1">
        <Manager name="2">
            <Employee name="3">
        </Manager>
        <Manager name="abe">
        </Manager>
        <Employee name="4">
        <Employee name="5">
    </Manager>
</root>

XML提供树视图,并且根据用户点击的树视图中的位置,我要么检索单击的员工(这很容易,因为我可以使用treeview.SelectedNode),或者以防万一单击是在根节点上,或者是管理器节点,即管理器下的第一个员工。

即。

  • 点击root应该会显示Employee 4的详细信息(第一个员工记录直接位于Manager 1下的root下)。
  • 点击Manager 1也应该显示Employee 4
  • 点击Manager 2会显示Employee 3
  • 点击Manager Abe不会产生任何结果。
  • Employee 5仅显示直接点击该员工的时间。

Manager 1也可能没有任何直接雇员。在这种情况下,单击root应该产生第一个具有员工的经理下的第一个Employee。因此,如果我们假设Employee 4Employee 5不在Manager 1下,则点击root会产生Employee 3

我尝试使用ElementElementsDescendantDescendants的一些不同变体,并且有点卡住了。

我想我可以为每个单独的组合编写方案(即rootClickedmanagerClickedemployeeClicked),这就是我原来做的,但我正在寻找代码方面,希望更容易维护的东西。

我非常希望使用root.Element(&#34; Employee&#34;)会有所帮助,但是会引发Could not find an implementation of the query pattern for source type 'System.Xml.Linq.XEelement'. 'select' not found错误。

有人能够为我提供解决我的问题所需要的那么小的推动吗?

2 个答案:

答案 0 :(得分:4)

使用XPath

ClickedNode.XPathEvaluate
    ("self::Employee | self::Manager/Employee[1] | self::root/Manager[1]/Employee[1]")

<强>更新

回答编辑过的问题:

使用

 ClickedNode[self::Employee]
|
 ClickedNode[not(self::Employee)]/descendant-or-self::Manager[Employee][1]/Employee[1]

选择

一个。单击的节点,如果是“员工”

或者:

湾点击节点的第一个desendant-or-self的第一个子Employee,即经理并且有一个孩子Employee

并且,如果ClickedNode是XElement(或XNode),那么

  ClickedNode.XPathEvaluate
        (self::*[self::Employee]
      | self::node()[not(self::Employee)]
                       /descendant-or-self::Manager[Employee][1]/Employee[1]
        )

最后,这是一个完整的C#代码

名为TestLinqXpath 的静态类:

using System.Xml.Linq;
using System.Xml.XPath;

namespace TestLinqXpath
{
    public  static class TestLinqXpath
    {
        public static XElement SelectNearestDescendantEmployee(XElement clicked)
        {
            string Expr =
@"(self::*[self::Employee]
 | self::node()[not(self::Employee)]
           /descendant-or-self::Manager[Employee][1]/Employee[1]
   )";
            XElement result = clicked.XPathSelectElement(Expr);

            return result;
        }
    }
}

并进行广泛测试

using System;
using System.Xml.Linq;
using System.Xml.XPath;

namespace TestLINQ_Xml
{
    class Program
    {
        static void Main(string[] args)
        {
            Test();
        }

        static void Test()
        {
            string xml = 
@"<root>
    <Manager name='1'>
        <Manager name='2'>
            <Employee name='3'/>
        </Manager>
        <Manager name='abe'>
        </Manager>
        <Employee name='4'/>
        <Employee name='5'/>
    </Manager>
</root>";
            XElement top = XElement.Parse(xml);

            XElement cliked1 = top;
            XElement res1 = TestLinqXpath.TestLinqXpath.SelectNearestDescendantEmployee(cliked1);
            Console.WriteLine(res1.ToString());

            XElement cliked2 = top.XPathSelectElement("Manager[@name='1']");
            XElement res2 = TestLinqXpath.TestLinqXpath.SelectNearestDescendantEmployee(cliked2);
            Console.WriteLine(res2.ToString());

            XElement cliked3 = top.XPathSelectElement(".//Manager[@name='2']");
            XElement res3 = TestLinqXpath.TestLinqXpath.SelectNearestDescendantEmployee(cliked3);
            Console.WriteLine(res3.ToString());

            XElement cliked4 = top.XPathSelectElement(".//Manager[@name='abe']");
            XElement res4 = TestLinqXpath.TestLinqXpath.SelectNearestDescendantEmployee(cliked4);
            Console.WriteLine((res4 != null) ? res4.ToString() : "null");

            XElement cliked5 = top.XPathSelectElement(".//Employee[@name='5']");
            XElement res5 = TestLinqXpath.TestLinqXpath.SelectNearestDescendantEmployee(cliked5);
            Console.WriteLine(res5.ToString());
        }
    }
}

运行此测试时,会生成所需的正确结果:

<Employee name="4" />
<Employee name="4" />
<Employee name="3" />
null
<Employee name="5" />

答案 1 :(得分:1)

linq2xml 应该

doc.Descendants("Manager").Where(n=>n.Attribute("name").Value=="yourManagerID")
.Select(
x=>x.Elements("Employee").Count()!=0 
?
x.Elements("Employee").First().Attribute("name").Value
:
x.Elements("Manager").First().Element("Employee").Atrribute("name").Value
);