具有复杂过滤 LINQ to XML c# 的查询

Queries with complex filtering LINQ to XML c#

我想了解 LINQ 的复杂过滤 XML。 我创建了一个简单的 XML 示例 (DataBaseCities.xml):

<?xml version="1.0" encoding="utf-8"?>
<DataBase>
    <DocumentInfo version="1.0" schemaVersion="1.0"/>
    <ListOfMegaCities>
        <MegaCities city="Moscow" continent="Europe">
            <VariantConstraint>
                <LanguageRef LanguageId="russian">
                    <LanguageDialectsRef DialectsId="north"/>
                </LanguageRef>
            </VariantConstraint>
            <Districts>                
                <CityDistrict District="Arbat"/>
                <CityDistrict District="Basmanny"/>
            </Districts>
        </MegaCities>
        <MegaCities city="New York" continent="North America">
            <VariantConstraint>
                <LanguageRef LanguageId="english">
                    <LanguageDialectsRef DialectsId="west"/>
                </LanguageRef>
                <LanguageRef LanguageId="spanish">
                    <LanguageDialectsRef DialectsId="cental"/>
                </LanguageRef>              
            </VariantConstraint>
            <Districts>                
                <CityDistrict District="Queens"/>
                <CityDistrict District="Bronx"/>
            </Districts>
        </MegaCities>
        <MegaCities city="London" continent="Europe">
            <VariantConstraint>
                <LanguageRef LanguageId="english">
                    <LanguageDialectsRef DialectsId="west"/>
                </LanguageRef>
                <LanguageRef LanguageId="spanish">
                    <LanguageDialectsRef DialectsId="central"/>
                </LanguageRef>  
                <LanguageRef LanguageId="french">
                    <LanguageDialectsRef DialectsId="central"/>
                </LanguageRef>              
            </VariantConstraint>
            <Districts>                
                <CityDistrict District="Greenwich"/>
                <CityDistrict District="Westminster"/>
            </Districts>
        </MegaCities>       
    </ListOfMegaCities>
</DataBase>

我尝试像这样过滤:

        XElement root = XElement.Load(@"DataBaseCities.xml");
        IEnumerable<XElement> ListOfMegaCities =
            from el in root.Descendants("MegaCities")
            where
                (from add in el.Descendants("LanguageRef")
                 where
                      (string)add.Attribute("LanguageId") == "english"
                 select add)
            .Any()
            select el;

        foreach (XElement el in ListOfMegaCities)
        {
            Console.WriteLine((string)el.Attribute("city"));
        }

所以输出是:

New York
London

但我想过滤不止一个属性。

  1. 如果我尝试使用这些行进行过滤:

    (string)add.Attribute("LanguageId") == "英语" && (字符串)add.Attribute(“LanguageId”)==“西班牙语”

为什么它不起作用?

  1. 如何过滤“DialectsId”?

示例:我想通过以下过滤获得“纽约”:

LanguageId="english"
DialectsId="west"
LanguageId="spanish"
DialectsId="cental"

也许有人有复杂过滤的好资源?我找到了这个 https://docs.microsoft.com/de-de/dotnet/standard/linq/write-queries-complex-filtering 但这只对我有部分帮助...

首先,请参阅@zaggler 评论。反序列化和操作对象会更容易。

其次,如果您想要只说英语和西班牙语的城市,请参阅@AkshayGaonkar 的回答。如果您想要说英语和西班牙语(也许还有其他语言)的城市,请参阅我的回答。


为了写复杂的Linq查询,我开始写simulacrum SQL :

-- I want all cities
select * from MegaCities as el
where
    -- that speak english with west dialect
    exists (
        select * from el.LanguageRef as add
        where add.LanguageId = 'english' and
            add.LanguageDialectsRef.DialectsId = 'west'
    )
    -- and also speak spanish with central dialect
    and exists (
        select * from el.LanguageRef as add
        where add.LanguageId == 'spanish' and
            add.LanguageDialectsRef.DialectsId = 'cental'
    )

然后我可以转置到 Linq :

IEnumerable<XElement> ListOfMegaCities =
    from el in root.Descendants("MegaCities")
    where
        (from add in el.Descendants("LanguageRef")
         where add.Attribute("LanguageId").Value == "english" &&
            add.Element("LanguageDialectsRef").Attribute("DialectsId").Value == "west"
         select add)
        .Any()
        &&
        (from add in el.Descendants("LanguageRef")
         where add.Attribute("LanguageId").Value == "spanish" &&
            add.Element("LanguageDialectsRef").Attribute("DialectsId").Value == "cental"
         select add)
        .Any()
    select el;

也许不是最好的技巧,但有时会有所帮助。

我使用了All而不是Any,因此它只过滤那些满足所有条件的城市。我习惯于编写 LINQ 表达式而不是 LINQ 查询。

List<XElement> ListOfMegaCities = root.Descendants("MegaCities")
                                 .Where(lr => lr.Descendants("LanguageRef")
                                 .All(
                                      li => (li.Attribute("LanguageId").Value == "english" 
                                          && li.Element("LanguageDialectsRef").Attribute("DialectsId").Value == "west")
                                          || (li.Attribute("LanguageId").Value == "spanish" 
                                          && li.Element("LanguageDialectsRef").Attribute("DialectsId").Value == "cental")
                                            )                                     
                                 ).ToList();