【问题标题】:How to select child nodes at unknown child element location using LINQ如何使用 LINQ 在未知子元素位置选择子节点
【发布时间】:2012-10-21 19:44:50
【问题描述】:

我的 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("Employee") 会有所帮助,但这会引发 Could not find an implementation of the query pattern for source type 'System.Xml.Linq.XEelement'. 'select' not found 错误。

谁能帮我解决我的问题所需的一点点帮助?

【问题讨论】:

    标签: c# xml linq xpath


    【解决方案1】:

    使用 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]
    

    这会选择

    一个。点击的节点,如果是`Employee'

    或者:

    b.被点击节点的第一个子孙的第一个子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" />
    

    【讨论】:

    • 我正在尝试将结果作为 XElement 返回,但在尝试您的解决方案时出现以下错误:Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function.。不过语法看起来很有希望。
    • @SchmitzIT,表达式中有错字——现在试试吧。
    • 谢谢 :) 但是,当我尝试将其作为 XElement 检索时,它仍然会引发错误:
      Unable to cast object of type 'd__0`1[System.Object]' to输入“System.Xml.Linq.XElement”。
    • @SchmitzIT,正如错误消息所解释的,XPathEvaluate 的结果是object 类型。它必须 cast 为预期的结果对象类型。我建议您阅读相应的 MSDN 文档,包括具体的代码示例。
    • 你能再推我一点吗?我真的发现缺少文档。我现在使用一种解决方法,仅在单击实际员工的情况下显示详细信息,而在单击经理节点时不显示任何内容,但从长远来看,我不介意以正确的方式解决此问题。
    【解决方案2】:

    这个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
    );
    

    【讨论】:

    • 感谢您的建议。不幸的是,看到我们正在响应可能在任何给定位置发生的树视图中的点击,我们事先并不知道 ManagerID。
    • @SchmitzIT 你可以用所选的 managerid 替换 yourManagerID
    • 我一定是哑巴什么的,因为我似乎无法弄清楚这一点。我正在尝试将查询结果填充到 XElement 中,并尝试将其转换为这样,但最终收到一条错误消息:Unable to cast object of type 'WhereSelectEnumerableIterator2[System.Xml.Linq.XElement,System.String]' to type 'System.Xml.Linq.XElement'.`我正在考虑忘记自动查找第一个员工的整个概念,并且仅在单击的节点是员工的情况下返回结果
    猜你喜欢
    • 2011-04-21
    • 1970-01-01
    • 1970-01-01
    • 2012-07-24
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多