为啥对 XML 文件的 Linq 查询只检查许多参数的第一个参数?

Posted

技术标签:

【中文标题】为啥对 XML 文件的 Linq 查询只检查许多参数的第一个参数?【英文标题】:Why does a Linq query on XML file checks only the first parameter of many?为什么对 XML 文件的 Linq 查询只检查许多参数的第一个参数? 【发布时间】:2020-09-15 13:33:50 【问题描述】:

我的源文件是一个 XML 文件,它看起来像这样,但有点大:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Controller Name="PLC1_0">
    <LibSolution>CMG</LibSolution>
    <LibName>1756-LxxES</LibName>       
    <LibVersion></LibVersion>
    <Parameter Name="ChassisName">Local</Parameter>
    <Parameter Name="Slot">0</Parameter>
    <Parameter Name="Size">4</Parameter>
    <Parameter Name="SoftwareRevision">31</Parameter>
    <Parameter Name="ProcessorType">1756-L83ES</Parameter>
    <Parameter Name="ConfigureMotion">1</Parameter>
    <Parameter Name="MotionGroupName">MotionGroup1</Parameter>
    <!--====================================================================================================================-->
    <!-- IO CONFIGURATION -->
    <!--====================================================================================================================-->
    <IOInstances>
        <Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0907_2">             
            <LibSolution>CMG</LibSolution>          
            <LibName>1734-IB4/C</LibName>       
            <LibVersion></LibVersion>
            <ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
            <Parameter Name="IP">80</Parameter>
            <Parameter Name="Slot">6</Parameter>
            <Parameter Name="ProgNameIO">CLX1_IO</Parameter>
        </Instance>
        
        <Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0907_4">             
            <LibSolution>CMG</LibSolution>          
            <LibName>1734-IB4/C</LibName>       
            <LibVersion></LibVersion>
            <ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
            <Parameter Name="IP">80</Parameter>
            <Parameter Name="Slot">7</Parameter>
            <Parameter Name="ProgNameIO">CLX1_IO</Parameter>
        </Instance>
        
        <Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0907_5">             
            <LibSolution>CMG</LibSolution>          
            <LibName>1734-4IOL/A</LibName>      
            <LibVersion></LibVersion>
            <ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
            <Parameter Name="Slot">8</Parameter>
        </Instance>
        
        <Instance ModuleName="GEN1" UnitName="CAB1" SubUnitName="KF0910_1">             
            <LibSolution>CMG</LibSolution>          
            <LibName>1734-OB4/C</LibName>       
            <LibVersion></LibVersion>
            <ParentModule>CLO1_CAB1_KF0901_1</ParentModule>
            <Parameter Name="IP">80</Parameter>
            <Parameter Name="Slot">9</Parameter>
            <Parameter Name="ProgNameIO">CLX1_IO</Parameter>
        </Instance>         
    </IOInstances>          
</Controller>   
</root>

我的查询以实例为目标,例如具有特殊名称的参数。问题是看起来 LinQ 不像普通 SQL 那样工作。

此查询应返回具有 ModuleName=GEN1Parameter 的所有实例 --> [Attribute] "Name" -- > [值] "插槽"

IEnumerable<XElement> finalObject = from node in xDocConfig.Descendants("IOInstances").Elements("Instance")
                                      where node.Attribute("ModuleName").Value.Equals("GEN1")
                                      where node.Element("Parameter").Attribute("Name").Value.Equals("Slot")
                                      select node;

不幸的是,它只返回我的示例 XML 的倒数第二个实例。巧合的是,在这种情况下,缺少一些参数,因此参数“slot”是第一个参数,并通过我的查询找到。如果是第二个或第三个参数,则查询未找到。

我真的不知道如何更改我的查询以获取所有 4 个实例。感谢您的帮助。

【问题讨论】:

嗯,你发布的 xml 给了我一个错误Unexpected end of file has occurred. The following elements are not closed: root. Line 56, position 17 node.Element("Parameter") 将获得第一个具有该名称的元素,您想在所有元素中搜索具有特定属性的元素。 对不起,root 的结束标签在那里但不可见。我不知道为什么... 【参考方案1】:

你在哪里:

where node.Element("Parameter")

这似乎只针对与名称 "Parameter" 匹配的第一个元素。

我相信有一种替代方法称为.Elements(),您必须使用它,因为Instance 节点中有多个Parameter 元素。

我相信它将返回一个 IEnumerable,因此您必须更改逻辑以检查属性 Name 等于 Slot 的所有 Parameter 节点。

https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xcontainer.elements?view=netcore-3.1#System_Xml_Linq_XContainer_Elements_System_Xml_Linq_XName_

【讨论】:

具体node.Elements("Paramter").Any(e =&gt; e.Attribute("Name").Value.Equals("Slot")) Juharr,您的提示引导我找到解决方案,请回答问题,我会将其标记为最有帮助的。 IEnumerable finalObject = from node in xDocConfig.Descendants("IOInstances").Elements("Instance") where node.Attribute("ModuleName").Value.Equals("GEN1") where node.Elements("Parameter" ).Any(x => x.Attribute("Name").Value.Equals("Slot")) 选择节点;【参考方案2】:

更新误解了问题,要返回实际的实例,这样做:

IEnumerable<XElement> finalObject = xDocConfig.Descendants("IOInstances")
    .SelectMany(x => x.Elements("Instance").Where(i => i.Attribute("ModuleName").Value.Equals("GEN1")
    &&
    i.Elements("Parameter").Any(p => p.Attribute("Name").Value.Equals("Slot"))
    ));

基本上,您需要SelectMany 来展平嵌套集合,Any() 来查找其中包含元素的集合。

【讨论】:

这会导致 Parameter 元素而不是 Instance 元素的集合。【参考方案3】:

请尝试以下方法。

c#

void Main()

    const string fileName = @"e:\temp\prototype.xml";
    
    XDocument xdoc = XDocument.Load(fileName);

    IEnumerable<XElement> finalObject = xdoc.Descendants("IOInstances")
        .SelectMany(x => x.Elements("Instance")
            .Where(i => i.Attribute("ModuleName").Value.Equals("GEN1")
            &&
            i.Elements("Parameter").Any(p => p.Attribute("Name").Value.Equals("Slot"))
            )
        );

    finalObject.Dump();

【讨论】:

这与 OP 已经拥有的基本相同,并且只会检查每个实例中的第一个 Paramter 元素。 @juharr。你是对的。我带来了一个现在正在运行的解决方案。它现在提供了许多 &lt;Instance&gt; 元素。【参考方案4】:

@Juharr,您的提示引导我找到了一个解决方案,这是最有帮助且非常简单的。

IEnumerable<XElement> finalObject = from node in xDocConfig.Descendants("IOInstances").Elements("Instance") 
                                    where node.Attribute("ModuleName").Value.Equals("GEN1") 
                                    where node.Elements("Parameter").Any(x => x.Attribute("Name").Value.Equals("Slot")) 
                                    select node;
第一行返回一个枚举。 第二行过滤这个 枚举,因为只有一个具有该名称的属性。 第三行必须包含一个“映射”,因为要检查的参数不止一个。 lambda 表达式遍历所有参数,并且 .Any() 将 bool 值返回到 WHERE 表达式。

谢谢大家,感谢你们的努力。

【讨论】:

以上是关于为啥对 XML 文件的 Linq 查询只检查许多参数的第一个参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 xml 数据的 linq 查询中使用 TryParse?

为啥我不能对 Visio Masters 集合使用 Linq 查询?

Linq To Xml操作XML增删改查

LINQ

LINQ

Linq简介