Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实现LINQ到XML查询的更好方法?_C#_.net_Linq To Xml - Fatal编程技术网

C# 实现LINQ到XML查询的更好方法?

C# 实现LINQ到XML查询的更好方法?,c#,.net,linq-to-xml,C#,.net,Linq To Xml,假设我有这个XML文件: <?xml version="1.0" encoding="utf-8" standalone="yes"?> <Root> <Category Name="Tasties"> <Category Name="Pasta"> <Category Name="Chicken"> <Recipe Name="Chicken and Shrimp Scampi" />

假设我有这个XML文件:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root>
  <Category Name="Tasties">
    <Category Name="Pasta">
      <Category Name="Chicken">
        <Recipe Name="Chicken and Shrimp Scampi" />
        <Recipe Name="Chicken Fettucini Alfredo" />
      </Category>
      <Category Name="Beef">
        <Recipe Name="Spaghetti and Meatballs" />
        <Recipe Name="Lasagna" />
      </Category>
      <Category Name="Pork">
        <Recipe Name="Lasagna" />
      </Category>
      <Category Name="Seafood">
        <Recipe Name="Chicken and Shrimp Scampi" />
      </Category>
    </Category>
  </Category>
</Root>
哪种方法有效,尽管它不检查路径,只检查第一个名为Chicken的类别。我可以递归地挖掘路径中的每个元素,但似乎我缺少了一个更好的解决方案。另外,当我只需要一个
IEnumerable
时,我当前的查询返回
IEnumerable


基本上我可以让它工作,但它看起来很混乱,我希望看到任何LINQ建议或技术来更好地进行查询。

就我个人而言,我会使用
XmlDocument
和熟悉的
SelectNodes

foreach(XmlElement el in doc.DocumentElement.SelectNodes(
   "Category[@Name='Tasties']/Category[@Name='Pasta']/Category[@Name='Chicken']/Recipe")) {
    Console.WriteLine(el.GetAttribute("Name"));
}
对于LINQ到XML,我猜(未经测试)类似于:

var q = from c1 in doc.Root.Elements("Category")
        where c1.Attribute("Name").Value == "Tasties"
        from c2 in c1.Elements("Category")
        where c2.Attribute("Name").Value == "Pasta"
        from c3 in c2.Elements("Category")
        where c3.Attribute("Name").Value == "Chicken"
        from recipe in c3.Elements("Recipe")
        select recipe.Attribute("Name").Value;
foreach (string name in q) {
    Console.WriteLine(name);
}

编辑:如果希望类别选择更加灵活:

    string[] categories = { "Tasties", "Pasta", "Chicken" };
    XDocument doc = XDocument.Parse(xml);
    IEnumerable<XElement> query = doc.Elements();
    foreach (string category in categories) {
        string tmp = category;
        query = query.Elements("Category")
            .Where(c => c.Attribute("Name").Value == tmp);
    }
    foreach (string name in query.Descendants("Recipe")
        .Select(r => r.Attribute("Name").Value)) {
        Console.WriteLine(name);
    }
这可能不明显,但由于
中的
不会立即求值,因此直到很久以后才会检查
类别的值(通过谓词
AnonMethod
)。这是C#spec精确细节的不幸结果。引入
tmp
范围在foreach中)意味着每次迭代都会捕获:

class SecondWrapper {
    public string tmp;
    public bool AnonMethod(XElement c) {
        return c.Attribute("Name").Value == tmp;
    }
}
...
string category;
using(var iter = categories.GetEnumerator()) {
    while(iter.MoveNext()) {
        category = iter.Current;
        SecondWrapper wrapper = new SecondWrapper(); // note 1 per iteration
        wrapper.tmp = category;
        query = query.Elements("Category")
             .Where(wrapper.AnonMethod);
    }
}

因此,我们现在评估还是以后评估并不重要。复杂而混乱。你可以看到为什么我喜欢改变规格

我个人会使用
XmlDocument
和熟悉的
SelectNodes

foreach(XmlElement el in doc.DocumentElement.SelectNodes(
   "Category[@Name='Tasties']/Category[@Name='Pasta']/Category[@Name='Chicken']/Recipe")) {
    Console.WriteLine(el.GetAttribute("Name"));
}
对于LINQ到XML,我猜(未经测试)类似于:

var q = from c1 in doc.Root.Elements("Category")
        where c1.Attribute("Name").Value == "Tasties"
        from c2 in c1.Elements("Category")
        where c2.Attribute("Name").Value == "Pasta"
        from c3 in c2.Elements("Category")
        where c3.Attribute("Name").Value == "Chicken"
        from recipe in c3.Elements("Recipe")
        select recipe.Attribute("Name").Value;
foreach (string name in q) {
    Console.WriteLine(name);
}

编辑:如果希望类别选择更加灵活:

    string[] categories = { "Tasties", "Pasta", "Chicken" };
    XDocument doc = XDocument.Parse(xml);
    IEnumerable<XElement> query = doc.Elements();
    foreach (string category in categories) {
        string tmp = category;
        query = query.Elements("Category")
            .Where(c => c.Attribute("Name").Value == tmp);
    }
    foreach (string name in query.Descendants("Recipe")
        .Select(r => r.Attribute("Name").Value)) {
        Console.WriteLine(name);
    }
这可能不明显,但由于
中的
不会立即求值,因此直到很久以后才会检查
类别的值(通过谓词
AnonMethod
)。这是C#spec精确细节的不幸结果。引入
tmp
范围在foreach中)意味着每次迭代都会捕获:

class SecondWrapper {
    public string tmp;
    public bool AnonMethod(XElement c) {
        return c.Attribute("Name").Value == tmp;
    }
}
...
string category;
using(var iter = categories.GetEnumerator()) {
    while(iter.MoveNext()) {
        category = iter.Current;
        SecondWrapper wrapper = new SecondWrapper(); // note 1 per iteration
        wrapper.tmp = category;
        query = query.Elements("Category")
             .Where(wrapper.AnonMethod);
    }
}

因此,我们现在评估还是以后评估并不重要。复杂而混乱。你可以看到为什么我喜欢改变规格

这里的代码与Marc的第二个示例类似,但经过测试和验证

var q = from t in doc.Root.Elements("Category")
        where t.Attribute("Name").Value == "Tasties"
        from p in t.Elements("Category")
        where p.Attribute("Name").Value == "Pasta"
        from c in p.Elements("Category")
        where c.Attribute("Name").Value == "Chicken"
        from r in c.Elements("Recipe")
        select r.Attribute("Name").Value;

foreach (string recipe in q)
{
    Console.WriteLine("Recipe name = {0}", recipe);
}

一般来说,我认为在LINQ查询中只需要一个
select
语句。由于嵌套的select语句,您得到了
IEnumerable

这里的代码与Marc的第二个示例类似,但经过测试和验证

var q = from t in doc.Root.Elements("Category")
        where t.Attribute("Name").Value == "Tasties"
        from p in t.Elements("Category")
        where p.Attribute("Name").Value == "Pasta"
        from c in p.Elements("Category")
        where c.Attribute("Name").Value == "Chicken"
        from r in c.Elements("Recipe")
        select r.Attribute("Name").Value;

foreach (string recipe in q)
{
    Console.WriteLine("Recipe name = {0}", recipe);
}
一般来说,我认为在LINQ查询中只需要一个
select
语句。由于嵌套的select语句,您得到的是
IEnumerable

如果您选择System.Xml.XPath,则将向XDocument添加XPathSelectElements()扩展方法。这将允许您使用XPath语句选择节点,如果您对此比较满意的话

否则,您可以使用SelectMany将IEnumerable
展平为一个IEnumerable

IEnumerable<IEnumerable<String>> foo = myLinqResults;
IEnumerable<string> bar = foo.SelectMany(x => x);
IEnumerable foo=myLinqResults;
IEnumerable bar=foo.SelectMany(x=>x);
如果您选择System.Xml.XPath,这将向XDocument添加XPathSelectElements()扩展方法。这将允许您使用XPath语句选择节点,如果您对此比较满意的话

否则,您可以使用SelectMany将IEnumerable
展平为一个IEnumerable

IEnumerable<IEnumerable<String>> foo = myLinqResults;
IEnumerable<string> bar = foo.SelectMany(x => x);
IEnumerable foo=myLinqResults;
IEnumerable bar=foo.SelectMany(x=>x);

有点晚了,但扩展方法确实有助于清理外观凌乱的LINQ-to-XML查询。对于您的场景,您可以使用如下代码:

var query = xml.Root
               .Category("Tasties")
               .Category("Pasta")
               .Category("Chicken")
               .Recipes();

。。。使用我在

中介绍的一些技术有点晚了,但是扩展方法确实可以帮助清理外观混乱的LINQ-to-XML查询。对于您的场景,您可以使用如下代码:

var query = xml.Root
               .Category("Tasties")
               .Category("Pasta")
               .Category("Chicken")
               .Recipes();

。。。使用我在

中介绍的一些技术,这是VB.NET的一大优势-您可以使用原生ASP样式的XML文本和Linq2XML查询,而无需通过方法和字符串访问属性等;-):p尽管我不能忍受VB语法的其余部分,如果我不得不读的话,我可以读它,但每次都会让我畏缩。你的句子“我当前的查询也返回IEnumerable>,而我只需要一个IEnumerable。”似乎遗漏了什么。为了保留尖括号,用`像这样的字符
IEnumerable
。哦,我甚至没有注意到,我会解决这个问题注意:我为级别数量灵活的情况添加了一个不同的示例,这是VB.NET的最大优点之一-您可以使用原生ASP样式的XML文本和Linq2XML查询,而无需通过方法和字符串访问属性等;-):p尽管我不能忍受VB语法的其余部分,如果我不得不读的话,我可以读它,但每次都会让我畏缩。你的句子“我当前的查询也返回IEnumerable>,而我只需要一个IEnumerable。”似乎遗漏了什么。为了保留尖括号,用`characters this
IEnumerable
。哦,我甚至没有注意到,我将修复这个问题。注意,我添加了一个不同的示例,用于级别数灵活的情况。很酷,我不知道DocumentElement.SelectNodes()(我对xml编程相当陌生)(注意我将LINQ改为XML)@Davy8-第一个示例是XmlDocument,第二个示例显示XDocumentRight,第二个答案更长,但更容易以更强类型的方式重构为可重用的东西。是的;LINQ“Where”方法使用延迟执行。“foreach”construct捕获左值;这意味着如果“foreach”中没有“tmp”作用域,则为每个级别捕获相同的变量-这通常意味着最后一个值(“Chicken”)用于每个级别。尝试删除它;您将看到没有匹配项。整个“foreach”/“左值”/captured variable issue是一个已知的问题。我过去曾与Edic+Mads交谈过,看看是否可能是cha