【问题标题】:Linq xml recursive selectLinq xml递归选择
【发布时间】:2013-07-25 17:26:54
【问题描述】:
<Nodes>
  <Node>
    <ID>1</ID>
    <TIDS>
      <TID>2</TID>
      <TID>3</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>2</ID>
    <TIDS>
      <TID>4</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>3</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>4</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>5</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>6</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>7</ID>
  </Node>
</Nodes>

我想编写查询,它将 fselect TID 并再次查询 xml 以选择它的 TID 假设我的 where 条件是 ID 等于 1 那么我想输出 2,3,4,7 在我的 where 条件下,如果我输入 ID 等于 5,然后输入 7 如何编写递归 linq

【问题讨论】:

  • 为什么应该(基于什么逻辑)为 ID=1 返回 2、3、4 和 7?
  • @Guanxi 我相信 OP 正在寻找的行为是 ID 1 返回 2 和 3,然后查找这两个 ID 继续。因此,查找 2 将产生 4,查找 3 将产生 7(继续查找 4 也会产生 7,查找 7 将不会显示任何内容)。总的来说,从 1 开始,函数“发现”的唯一 ID 将是 2、3、4 和 7。

标签: xml linq c#-4.0


【解决方案1】:
 var result = xml.Elements()
   // Find element with <ID>1</ID>
   .Where(x => x.Elements().Any(d => d.Name == "ID" && d.Value == "1"))
   // Find element <TIDS>
   .Elements().Where(x => x.Name == "TIDS")
   // Find elements <TID>
   .Elements().Where(x => x.Name == "TID")
   // Select values
   .Select(x => x.Value);

注意,我将xml 变量设为这样:

   XElement xml = XElement.Parse (@"<Nodes>
  <Node>
    <ID>1</ID>
    <TIDS>
      <TID>2</TID>
      <TID>3</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>2</ID>
    <TIDS>
      <TID>4</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>3</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>4</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>5</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>6</ID>
    <TIDS>
      <TID>7</TID>
    </TIDS>
  </Node>
  <Node>
    <ID>7</ID>
  </Node>
</Nodes>");

【讨论】:

  • @James - 谢谢,我不确定我做对了 - 但我认为如果你把我的代码放在一个函数中然后递归调用它,它可能就是他想要的。
  • 这仅返回初始 ID 的 TID 值。它不会再次查询每个 TID。
【解决方案2】:

递归 LINQ 更像是一种聚会技巧,而不是您真正想在生产代码中使用的任何东西,但这里有一种方法:

XElement nodes = /* load your xml */;

Func<XElement, int, IEnumerable<int>> query = null;

/* BAD CODE, DO NOT USE! */
query = (x,id) => 
   x.Elements("Node")
     .Where (node => (int)node.Element("ID") == id) // find our node
     .Descendants("TID")      // this will be empty in the base case
     .Select (tid => (int)tid)
     .SelectMany(tid => 
       query(x,tid)           // recurse
       .Concat(new[]{tid})    // keep the current TID if its node had no TIDs
     )
     .Distinct();

var resultOf7423 = query(nodes, 1);
var resultOf7 = query(nodes, 5);
/* END BAD CODE (I HOPE) */

这是令人费解且脆弱的代码,您几乎可以肯定不应该使用它。相反,您可以从 XElement 创建一个扩展方法:

public static IEnumerable<int> SelectRelatedIds(this XElement element, int id)
{
    if(element == null) throw new ArgumentNullException("element");
    return SelectRelatedIdsIterator(element, id)
                .Where(i => i != id)
                .Distinct();
}

private static IEnumerable<int> SelectRelatedIdsIterator(XElement element, int id)
{
    yield return id;

    var tids = element.Elements("Node")
                .Where (node => (int)node.Element("ID") == id)
                .Descendants("TID");

    foreach (int tid in tids)
        foreach(var i in  SelectRelatedIdsIterator(element, tid))
            yield return i;
}

这使递归保持包含并且很容易理解(至少在您了解迭代器块时)。它也是 LINQ 友好的,并且可能以第一种方式不具备的方式进行组合。

【讨论】:

    【解决方案3】:

    函数GetTids为你传入的ID返回一个TIDs的列表。在传递初始ID之后,它返回一个列表(tidIndex)。然后运行一个循环,每个TID 值用于查询XML 并添加到tidList

    List<string> TidList()
    {
        var xml = XDocument.Load(@"C:\PathToXml\File.xml");
    
        var tidIndex = GetTids(xml, "1").ToList();
        var tidList = new List<string>(tidIndex);
    
        foreach (var tid in tidIndex)
            tidList.AddRange(GetTids(xml, tid));
    
        return tidList;
    }
    
    static IEnumerable<string> GetTids (XDocument xml, string id)
    {
        return xml.Descendants("Node")
            .Where(x => x.Element("ID").Value == id)
            .Descendants("TID")
            .Select (s => s.Value);
    }
    

    这会返回:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-17
      • 1970-01-01
      • 2013-01-17
      相关资源
      最近更新 更多