获取XML的XElement

4
这是我的XML文件:
<Applications>
   <Application Name="Abc">
     <Section Name="xyz">
        <Template Name="hello">
         ...
         ....
         </Template>
      </Section>
   </Application>
   <Application Name="Abc1">
     <Section Name="xyz1">
        <Template Name="hello">
         ...
         ....
         </Template>
      </Section>
   </Application>

我需要做的是根据Template标签的Name属性从给定的结构中获取Template XElement。问题是可能会有多个具有相同属性名称的template标签。区分因素是Application Name属性值和section属性值。

目前,我可以通过先基于其属性获取应用程序元素,然后基于其属性获取节,最后基于其名称获取模板来获取XElement。

我想知道是否有一种方法可以一次性获得它。


您可以通过构建和评估XPath表达式来实现这一点,但使用LINQ to XML和您当前的方法也是一种有效的策略。您想要“一次性”匹配元素的特定原因是什么? - Frédéric Hamidi
没有特别的原因,只是想知道是否有可能实现它。 - gizgok
4个回答

6
我会利用你可以调用Elements或现有序列的事实,因此:
var template = doc.Descendants("Application")
                  .Where(x => (string) x.Attribute("Name") == applicationName)
                  .Elements("Section")
                  .Where(x => (string) x.Attribute("Name") == sectionName)
                  .Elements("Template")
                  .Where(x => (string) x.Attribute("Name") == templateName)
                  .FirstOrDefault();

您可能需要在某个地方添加一个扩展方法:
public static IEnumerable<XElement> WithName(this IEnumerable<XElement> elements,
                                             string name)
{
    this elements.Where(x => (string) x.Attribute("Name") == name);
}

然后您可以将查询重写为:

var template = doc.Descendants("Application").WithName(applicationName)
                  .Elements("Section").WithName(sectionName)
                  .Elements("Template").WithName(templateName)
                  .FirstOrDefault();

...我认为您会同意这很容易阅读 :)

请注意,使用将XAttribute强制转换为string而不是使用Value属性的方法意味着任何没有Name属性的元素都将被有效地忽略,而不会导致NullReferenceException


整洁,加一点扩展方法,我不知道那个。谢谢。 - gizgok

1
XPath应该能帮到你。使用Extensions.XPathSelectElement Method (XNode, String)
XDocument xdoc = XDocument.Load("yourfile.xml");
string xPathQuery = string.Format(
    "/Applications/Application[@Name='{0}']/Section[@Name='{1}']/Template[@Name='{2}']",
    "MyApplication",
    "MySection",
    "MyTemplate"
);

XElement template = xdoc.Root.XPathSelectElement(xPathQuery);

由于节点的层次结构已知,我更倾向于使用 xDoc.XPathSelectElements("/Applications/Application/Section/Template[@Name='" + templateName+ "']"); 这个方法,这应该会更有效率。 - nulltoken
@nulltoken:这不符合原帖作者的要求。 - Daniel Hilgarth
@DanielHilgarth 你说得对。我确实忘记为 ApplicationSection 添加命名限制了。我的意思是,最好使用从根目录开始的显式路径,而不是使用 "//template[@Name='xxxx']" 查询。不过,看起来答案已经被修复了 :) - nulltoken
是的,我第一次误读了问题。我已经以这种方式更新了我的答案。 - Steve B

1
    XDocument doc = XDocument.Load("Path of xml");
    var selection =
        doc.Descendants("Section").Select(item => item).Where(
            item => item.Attribute("Name").Value.ToString().Equals("Section Name Value")).ToList();

    if(null != selection)
    {
        var template =
            selection.Descendants("Template").Select(item => item).Where(
            item => item.Attribute("Name").Value.ToString().Equals("Template name value"));
    }

我还没有尝试过你的答案,一旦我尝试过后会再联系你。 - gizgok

1
以下代码应该可以解决问题:
var template = doc.Descendants("Template")
                  .Where(x => x.Attribute("Name").Value == "hello"
                           && x.Parent.Attribute("Name").Value == "xyz1"
                           && x.Parent.Parent.Attribute("Name").Value == "Abc1");

请注意,如果XML不符合规范,此代码将抛出异常。具体来说,如果任何相关标签中不包含名为“Name”的属性,则会出现NullReferenceException。或者如果Template标记没有两个父级,则也会出现异常。

运行得很好,最后添加了FirstOrDefault()。 - gizgok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接