LINQ 4 XML - 在树结构中深入查询的正确方法是什么?

时间:2010-03-25 17:46:06

标签: linq-to-xml linq-to-objects

我有一个深度为4的XML结构:

<?xml version="1.0" encoding="utf-8"?>
<EmailRuleList xmlns:xsd="EmailRules.xsd">
  <TargetPST name="Tech Communities">
    <Parse emailAsList="true" useJustDomain="false" fromAddress="false" toAddress="true">
      <EmailRule address="@aspadvice.com" folder="Lists, ASP" saveAttachments="false" />
      <EmailRule address="@sqladvice.com" folder="Lists, SQL" saveAttachments="false" />
      <EmailRule address="@xmladvice.com" folder="Lists, XML" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="false" toAddress="true">
      <EmailRule address="northcoloradoarchitects@googlegroups.com" folder="Special Interest Groups|Northern Colorado Architects Group" saveAttachments="false" />
      <EmailRule address="spambayes@python.org" folder="Support|SpamBayes" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="true" toAddress="false">
      <EmailRule address="support@godaddy.com" folder="Support|GoDaddy" saveAttachments="false" />
      <EmailRule address="renew@no-ip.com" folder="Support|No-IP.com" saveAttachments="false" />
      <EmailRule address="discuss@orchardproject.net" folder="Discussions|Orchard Project" saveAttachments="false" />
    </Parse>
    <Parse emailAsList="false" useJustDomain="true" fromAddress="true" toAddress="false">
      <EmailRule address="@agilejournal.com"     folder="Newsletters|Agile Journal" saveAttachments="false"/>
      <EmailRule address="@axosoft.ccsend.com"   folder="Newsletters|Axosoft Newsletter" saveAttachments="false"/>
      <EmailRule address="@axosoft.com"          folder="Newsletters|Axosoft Newsletter" saveAttachments="false"/>
      <EmailRule address="@cmcrossroads.com"     folder="Newsletters|CM Crossroads" saveAttachments="false" />
      <EmailRule address="@urbancode.com"        folder="Newsletters|Urbancode" saveAttachments="false" />
      <EmailRule address="@urbancode.ccsend.com" folder="Newsletters|Urbancode" saveAttachments="false" />
      <EmailRule address="@Infragistics.com"     folder="Newsletters|Infragistics" saveAttachments="false" />
      <EmailRule address="@zdnet.online.com"     folder="Newsletters|ZDNet Tech Update Today" saveAttachments="false" />
      <EmailRule address="@sqlservercentral.com" folder="Newsletters|SQLServerCentral.com" saveAttachments="false" />
      <EmailRule address="@simple-talk.com"      folder="Newsletters|Simple-Talk Newsletter" saveAttachments="false" />
    </Parse>
  </TargetPST>
  <TargetPST name="[Sharpen the Saw]">
    <Parse emailAsList="false" useJustDomain="false" fromAddress="false" toAddress="true">
      <EmailRule address="rmiug-jobs@yahoogroups.com" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="inkedinusmc@yahoogroups.com" folder="Social|LinkedIn USMC" saveAttachments="false"/>
    </Parse>
    <Parse emailAsList="false" useJustDomain="false" fromAddress="true" toAddress="false">
      <EmailRule address="JobAlerts@CyberCoders.com" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="jobs@dice.com" folder="Head Geek|Job Alerts" saveAttachments="false" />
      <EmailRule address="news@cruisecritic.com" folder="Social|Cruise Critic" saveAttachments="false"/>
    </Parse>
    <Parse emailAsList="false" useJustDomain="true" fromAddress="true" toAddress="false">
      <EmailRule address="@moody.edu" folder="Social|5 Love Languages" saveAttachments="false" />
      <EmailRule address="@postmaster.twitter.com" folder="Social|Twitter" saveAttachments="false"/>
      <EmailRule address="@diabetes.org" folder="Physical|American Diabetes Association" saveAttachments="false"/>
      <EmailRule address="@membership.webshots.com" folder="Social|Webshots" saveAttachments="false"/>
    </Parse>
  </TargetPST>
</EmailRuleList>

现在,我有一个FromAddress和一个ToAddress,它是从传入的电子邮件中解析的。我想针对从此XML反序列化的类集执行LINQ查询。例如: ToAddress = asp@aspadvice.com FromAddress = keithb@sol3.net

查询:

  • 获取EmailRule.Include(Parse).Include(TargetPST)where address == ToAddress AND Parse.ToAddress == true AND Parse.useJustDomain == false
  • 获取EmailRule.Include(Parse).Include(TargetPST)where address == [ToAddress Domain Only] AND Parse.ToAddress == true AND Parse.useJustDomain == true
  • 获取EmailRule.Include(Parse).Include(TargetPST)where address == FromAddress AND Parse.FromAddress == true AND Parse.useJustDomain == false
  • 获取EmailRule.Include(Parse).Include(TargetPST)where address == [FromAddress Domain Only] AND Parse.FromAddress == true AND Parse.useJustDomain == true

我很难搞清楚这个LINQ查询。当然,我可以循环遍历XML中的所有位(包括反序列化到对象中):

XmlSerializer s = new XmlSerializer(typeof(EmailRuleList));
TextReader r = new StreamReader(path);
_emailRuleList = (EmailRuleList)s.Deserialize(r);

TargetPST[] PSTList = _emailRuleList.Items;
foreach (TargetPST targetPST in PSTList)
{
    olRoot = GetRootFolder(targetPST.name);
    if (olRoot != null)
    {
        Parse[] ParseList = targetPST.Items;
        foreach (Parse parseRules in ParseList)
        {
            EmailRule[] EmailRuleList = parseRules.Items;
            foreach (EmailRule targetFolders in EmailRuleList)
            {
            }
        }
    }
}

但是,这意味着要为每个地址遍历所有这些循环。查询对象对我来说更有意义。任何提示赞赏!

1 个答案:

答案 0 :(得分:1)

这里有你的代码:

XmlSerializer s = new XmlSerializer(typeof(EmailRuleList)); 
TextReader r = new StreamReader(path); 
_emailRuleList = (EmailRuleList)s.Deserialize(r); 

TargetPST[] PSTList = _emailRuleList.Items; 
foreach (TargetPST targetPST in PSTList) 
{ 
    olRoot = GetRootFolder(targetPST.name); 
    if (olRoot != null) 
    { 
        Parse[] ParseList = targetPST.Items; 
        foreach (Parse parseRules in ParseList) 
        { 
            EmailRule[] EmailRuleList = parseRules.Items; 
            foreach (EmailRule targetFolders in EmailRuleList) 
            { 
            } 
        } 
    } 
} 

在LINQ中实际上只是以下内容:

var query = from targetPST in _emailRuleList.Items
            let olRoot = GetRootFolder(targetPST.name)
            where olRoot != null
            from parseList in targetPST.Items
            from emailRule in parseList.Items
            select [whatever you want to select];

从这里开始,只需包含相应的where子句。在每个“级别”,您都可以访问先前在from子句中指定的对象,因此如果您想要执行其中一个查询(第一个),则可能是这样的:

where emailRule.address == ToAddress && 
    parseList.toAddress == true &&
    parseList.useJustDomain == false
select new {
    EmailRule = emailRule,
    Parse = parseList,
    TargetPST = targetPST
}

要在方法中这样做,我认为你的弊大于利(特别是在可读性方面),但在这里。关于为什么这么复杂的重要事项是嵌套的FROM子句转换为SelectMany,并且因为最终你需要EmailRule及其关联的父对象,你必须在一组嵌套的lambdas中进行所有选择为了引用父对象(因为子对象本身没有反向引用)。

_emailRuleList
    .Where( targetPst => GetRootFolder( targetPst.Name ) != null )
    .SelectMany( targetPst => {
        return targetPst.Items.SelectMany( parse => {
            return parse.Items.Select( rule => {
                return new {
                    TargetPST = targetPst,
                    Parse = parse,
                    EmailRule = rule
                };
            } );
        } );
    } )
    .Where( x => x.EmailRule.address == ToAddress &&
                 x.Parse.toAddress == true &&
                 x.Parse.useJustDomain == false );