是否可以使用 LINQ to XML 从此 XML 文件中提取所需的日期信息?

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

提问人:Andrew Truckle 提问时间:8/3/2023 最后编辑:Andrew Truckle 更新时间:8/3/2023 访问量:62

问:

这是否可以通过 LINQ to XML 实现?

我有一个对象,我获得了上次邀请日期早于今天的所有项目的列表:List<Congregation>

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

这是第 1 步。但是我还有另一个XML文件。下面是一个精简的示例(忽略日期,因为它们是编造的):

<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>

我知道这个XML设计得很糟糕(节点都有不同的名称),但这是我必须使用的数据文件。是否可以使用 LINQ to XML 从此 XML 中获取一组记录。WYYYYMMDD

例如,

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.
}

换句话说,我是否可以使用 LINQ to XML 来查找 () 在今天之前的最新日期 ()。这样我就可以更新到这个日期。MeetingDatecong.NamePublicTalkCongregation cong.LastInvited

有意义?


更新

我正在尝试建议的答案,但出现异常:

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

我应该澄清一下,一些较旧的分配历史记录条目不会有 \ 元素,应该绕过这些历史周的情况。MeetingDatePublicTalkCongregation

C# LINQ-to-XML

评论

1赞 NetMage 8/3/2023
您的示例具有相同的节点W20230504名称?和相同的 MeetingData 值?也许你应该发布一个更好的样本(包括一些空条目)?
0赞 Serge 8/3/2023
您的问题没有正确的细节。您必须发布更多包含所有可能情况的真实 xml 字符串。

答:

1赞 jdweng 8/3/2023 #1

请尝试以下操作:

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; }
}

评论

0赞 Andrew Truckle 8/3/2023
谢谢。请参阅我更新的问题。
0赞 jdweng 8/3/2023
尝试注释掉 meetingDate,看看这是否会导致问题。我更新了可能的解决方案。c# 中的 DateTime 不能为 null,我认为这是问题所在。
1赞 jdweng 8/3/2023
我测试了没有会议日期,代码有效。
0赞 Andrew Truckle 8/3/2023
我让它工作!棒。感谢您的帮助!👍
1赞 Serge 8/3/2023 #2

不幸的是,你的xml非常草率,所以无法理解你想要什么。

我一开始会创建一个数据集合

    List<Meeting> meetings = XElement.Parse(xml)
              .Descendants("PublicTalkInfo")
              .Select(x => new Meeting
              {
                  MeetingDate = x.Element("MeetingDate")?.Value == null ?
                           null : DateTime.Parse(x.Element("MeetingDate").Value),
                  Congregation = x.Element("PublicTalkCongregation")?.Value
              }).ToList();

public class Meeting
{
    public DateTime? MeetingDate { get; set; }
    public string Congregation { get; set; }
}

在此之后,我可以使用 LINQ 提取我想要的任何数据,例如

    Meeting meeting = meetings.Where(x => x.Congregation != "To Be Confirmed")
                              .OrderBy(x => x.MeetingDate)
                              .FirstOrDefault();
0赞 Andrew Truckle 8/3/2023 #3

根据提供的两个答案,这是我的最后一个函数(为了完整起见)。

public bool UpdateCongregationDatabase(string historyDatabase)
{
    bool bSaveDatabase = false;

    try
    {
        if (_Congregations.Count > 0)
        {
            var congregationsToReview = _Congregations
                        .Where(a => a.LastInvited < DateTime.Today);

            if(congregationsToReview.Count() > 0)
            {
                XDocument doc = XDocument.Load(historyDatabase);
                List<Meeting> meetings = doc
                              .Descendants("PublicTalkInfo")
                              .Select(x => new Meeting
                              {
                                  MeetingDate = x.Element("MeetingDate")?.Value == null ?
                                           new DateTime() : DateTime.Parse(x.Element("MeetingDate").Value),
                                  Congregation = x.Element("PublicTalkCongregation")?.Value
                              }).ToList();

                if (meetings.Count > 0)
                {
                    foreach (var congregation in congregationsToReview)
                    {
                        var historyForCongregation = meetings.Where(x => x.Congregation == congregation.Name && x.MeetingDate < DateTime.Today);
                        if (historyForCongregation.Count() > 0)
                        {
                            var lastInvitedMeeting = historyForCongregation.OrderByDescending(x => x.MeetingDate)
                                                                           .First();
                            if (lastInvitedMeeting != null &&
                                lastInvitedMeeting.MeetingDate > congregation.LastInvited)
                            {
                                congregation.LastInvited = (DateTime)lastInvitedMeeting.MeetingDate;
                                bSaveDatabase = true;
                            }
                        }

                        /*
                        var lastInvitedMeeting = meetings.Where(x => x.Congregation == congregation.Name && x.MeetingDate < DateTime.Today)
                                                         .OrderByDescending(x => x.MeetingDate)
                                                         .First();
                        if (lastInvitedMeeting != null)
                        {
                            congregation.LastInvited = (DateTime)lastInvitedMeeting.MeetingDate;
                            bSaveDatabase = true;
                        }
                        */
                    }
                }
            }
        }

    }
    catch (Exception ex)
    {
        SimpleLog.Log(ex);
    }

    return bSaveDatabase;
}

我不得不分解后处理代码以避免异常。不确定是否可以解决。