开发者

LINQ 4 XML - What is the proper way to query deep in the tree structure?

I have an XML structure that is 4 deep:

<?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="New开发者_开发知识库sletters|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>

Now, I have both an FromAddress and a ToAddress that is parsed from an incoming email. I would like to do a LINQ query against a class set that was deserialized from this XML. For instance: ToAddress = asp@aspadvice.com FromAddress = keithb@sol3.net

Query:

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

I am having a hard time figuring this LINQ query out. I can, of course, loop on all the bits in the XML like so (includes deserialization into objects):

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)
            {
            }
        }
    }
}

However, this means going through all these loops for each and every address. It makes more sense to me to query against the Objects. Any tips appreciated!


This code you have here:

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) 
            { 
            } 
        } 
    } 
} 

Is effectively just the following in 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];

From here, just include the appropriate where clauses. At every "level" you have access to the objects previously specified in a from clause, so if you wanted to do one of your queries (your first one), it'd be something like this:

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

To do this in methods, I think you're doing more harm than good (esp in readability), but here it is. The important thing to note on why this is so complicated is that a nested set of FROM clauses translates into a SelectMany, and because ultimately you need the EmailRule with its associated parent objects, you have to do all your selecting inside a set of nested lambdas in order to have references to the parent objects (since the child objects themselves do not have backreferences).

_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 );
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜