通过使用 LINQ 指定祖先节点从节点获取所有属性值

Posted

技术标签:

【中文标题】通过使用 LINQ 指定祖先节点从节点获取所有属性值【英文标题】:Getting all attribute values from node by specifying the ancestor node using LINQ 【发布时间】:2020-12-02 19:42:42 【问题描述】:

假设我有以下 xml 文件:

<ModuleSets>
    <ServiceModuleSet Name="serverModules">
        <Modules>
            <ServiceModule Name="ServerModule1"/>
            <ServiceModule Name="ServerModule2"/>
        </Modules>
    </ServiceModuleSet>
    <ServiceModuleSet Name="testBenchModules">
        <Modules>
            <ServiceModule Name="testBenchModule1"/>
            <ServiceModule Name="testBenchModule2"/>
        </Modules>
    </ServiceModuleSet>
    <ServiceModuleSet Name="hostComputerModules">
        <Modules>
        </Modules>
    </ServiceModuleSet>
</ModuleSets>

我想从ServiceModule 节点获取所有属性,方法是在ServiceModuleSet 节点中搜索,其中属性名称等于"serverModules"

我是 LINQ 新手并尝试了以下代码:

IEnumerable<string> allServerModules = from item in xmlDocument.Descendants("ServiceModuleSet")
    where item.Descendants("ServiceModuleSet").Any(attribute => attribute.Value == "serverModules")
    select (string)item.Attribute("Name");
    
foreach (var serverModule in allServerModules)

    Console.WriteLine(serverModule);

很遗憾,我没有将任何结果打印到控制台。使用 LINQ 解决它的正确方法是什么?

【问题讨论】:

docs.microsoft.com/en-us/dotnet/api/… 【参考方案1】:

这是我的解决方案建议:

var parsedXml = XDocument.Load("sample.xml");

var serverModules = parsedXml.Descendants("ServiceModuleSet")
    .FirstOrDefault(set => set.Attribute("Name")?.Value == "serverModules");

var moduleNames = serverModules?.Descendants("ServiceModule")
    .Select(module => module.Attribute("Name")?.Value)
    .Where(value => !string.IsNullOrEmpty(value));

if (moduleNames == null) return;

foreach (var moduleName in moduleNames)

    Console.WriteLine(moduleName);

    首先我尝试检索ServiceModuleSet,它的Name 属性等于"serverModules" 1.1 如果没有任何后代(满足过滤条件),那么我们不想失败,这就是为什么我使用FirstOrDefault 而不是First 1.2 Name 属性也应用了相同的容错逻辑,这就是我使用空条件运算符 (Attribute("Name")?.) 的原因 如果我找到了所需的节点,那么我可以寻找它的子节点来询问它们的Name 属性 2.1 再次使用空条件运算符 (serverModules?.Descendants) 使我们的代码更加健壮 2.2 ServiceModule 节点可能有也可能没有Name 属性,这就是为什么我结合使用空条件运算符(.Attribute("Name")?.)和空字符串过滤(!string.IsNullOrEmpty)。 如果我成功并检索了一些数据,那么我会打印它们,否则我会提前退出

【讨论】:

感谢您的解决方案和详细解释!就像我说的那样,我是这个话题的新手,昨天花了几个小时尝试和沮丧,但这正是我正在寻找的。我想我的错误是试图在一个 LINQ 语句中完成它,而不是在两个查询中拆分它。【参考方案2】:

我喜欢用字典:

            XDocument doc = XDocument.Load(FILENAME);

            Dictionary<string, List<string>> dict = doc.Descendants("ServiceModuleSet")
                .GroupBy(x => (string)x.Attribute("Name"), y => y)
                .ToDictionary(x => x.Key, y => y.Descendants("ServiceModule").Select(z => (string)z.Attribute("Name")).ToList());

【讨论】:

以上是关于通过使用 LINQ 指定祖先节点从节点获取所有属性值的主要内容,如果未能解决你的问题,请参考以下文章

如何从特定节点获取树祖先列表?

SpriteKit:如何遍历节点的祖先?

leetcode刷题13

XPTH定位总结

获取没有特定祖先 xml xpath 的节点

获取指定的元素节点