Search code examples
c#xmllinq-to-xml

Can I use LINQ to XML to extract the required date information from this XML file?


Is this possible to achieve with LINQ to XML?

I have a List<Congregation> object and I obtain a list of all items where the last invited date is older than today:

var congs =_CongregationData.Congregations
                            .Where(a => a.LastInvited < DateTime.Today)

That's step 1. But then I have another XML file. Here is a cut down sample (ignore the dates as they are made up):

<AssignmentHistory>
    <W20230731>
        <PublicTalkInfo>
            <PublicTalkTheme>Theme 1</PublicTalkTheme>
        </PublicTalkInfo>
    </W20230731>
    <W20230807>
        <PublicTalkInfo>
            <PublicTalkTheme>Theme 2</PublicTalkTheme>
        </PublicTalkInfo>
    </W20230807>
    <W20230814>
        <PublicTalkInfo>
            <MeetingDate>2023-08-20</MeetingDate>
            <PublicTalkCongregation>Congregation 1</PublicTalkCongregation>
            <PublicTalkTheme>Theme 3</PublicTalkTheme>
        </PublicTalkInfo>
    </W20230814>
    <W20230821>
        <PublicTalkInfo>
            <MeetingDate>2023-08-27</MeetingDate>
            <PublicTalkCongregation>Congregation 2</PublicTalkCongregation>
            <PublicTalkTheme>Theme 4</PublicTalkTheme>
        </PublicTalkInfo>
    </W20230821>
    <W20230828>
        <PublicTalkInfo>
            <MeetingDate>2023-09-03</MeetingDate>
            <PublicTalkCongregation>Congregation 3</PublicTalkCongregation>
            <PublicTalkTheme>Theme 5</PublicTalkTheme>
        </PublicTalkInfo>
    </W20230828>
</AssignmentHistory>

I know that this XML is badly designed (with the WYYYYMMDD nodes all having different names) but this is the data file I must work with. Can I use LINQ to XML to obtain a set of records from this XML.

Eg,

for(var cong : congs)
{
    // 1. Select from history XML all records where PublicTalkCongregation is cong.Name.
    // 2. Records must be date descending on MeetingDate.
    // 3. We only want the records older than today.
}

To put it another way, can I use LINQ to XML to find the most recent date (MeetingDate) for cong.Name (PublicTalkCongregation ) that is prior to today. So that I can then update cong.LastInvited to this date.

Make sense?


Update

I am trying the proposed answer but getting exception:

Unhandled Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: element
   at System.Xml.Linq.XElement.op_Explicit(XElement element)
   at ConsoleApp1.Program.<>c.<Main>b__2_0(XElement x) in D:\My Programs\2022\Backup MSA2\ConsoleApp1\ConsoleApp1\Program.cs:line 24
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at ConsoleApp1.Program.Main(String[] args) in D:\My Programs\2022\Backup MSA2\ConsoleApp1\ConsoleApp1\Program.cs:line 23

I should clarify that some of the older assignment history entries won't have the MeetingDate \ PublicTalkCongregation elements which case those history weeks should be bypassed.


Solution

  • Try following :

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml.Linq;
    using System.Globalization;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                XDocument doc = XDocument.Load(FILENAME);
                XElement assignments = doc.Descendants("AssignmentHistory").FirstOrDefault();
                List<Meeting> meetings = assignments.Elements().Select(x =>
                    new Meeting()
                    {
                        date = DateTime.ParseExact(x.Name.LocalName, "WyyyyMMdd", CultureInfo.InvariantCulture),
                        meetingDate = x.Descendants("MeetingDate").FirstOrDefault() == null ? new DateTime() : (DateTime)x.Descendants("MeetingDate").FirstOrDefault(),          
                        congregation = (string)x.Descendants("PublicTalkCongregation").FirstOrDefault()
                    }).ToList();
     
            }
                
        }
       
    }
    public class Meeting
    {
        public DateTime date { get; set; }
        public DateTime meetingDate { get; set; }
        public string congregation { get; set; }
    }