【问题标题】:Filter data from Xml according to date in C#根据C#中的日期过滤来自Xml的数据
【发布时间】:2012-09-22 22:44:54
【问题描述】:

我需要帮助根据日期过滤 xml 文件,我拥有的这部分代码仅根据用户名和地点打印出所有平均信息

        Runner run = new Runner();
       string filePath = "runners.xml"; //path
        XDocument xDoc = XDocument.Load(filePath);
        string userSelect = name;



        var averageAddDistancequery = xDoc.Descendants("User").Where(w => (string)w.Element("Name") == user).Select(s => new
        {
            add = s.Elements("Attempts").Where(w => (string)w.Element("Place").Value == "Paris").Select(t => t.Element("Distance").Value)
        }).ToList();

        if (averageAddDistancequery[0].add.Count() > 0) 
        {
            var aa = averageAddDistancequery[0].add.Average(a => float.Parse(a));
            run.averageDistance = aa.ToString();        
        }
        else
        {
           // nothing
        }

        var averageAdd2Distancequery = xDoc.Descendants("User").Where(w => (string)w.Element("Name") == userSelector).Select(s => new
        {
            add = s.Elements("Attempts").Where(w => (string)w.Element("Place").Value == "Madrid").Select(t => t.Element("Distance").Value)
        }).ToList();

        if (averageAdd2Distancequery[0].add.Count() > 0)
        {
            var aa = averageAdd2DistanceSubquery[0].add.Average(a => float.Parse(a));
            run.averageDistance2 = aa.ToString();
        }
        else
        {
           // nothing
        }



        xmlDoc.DocumentElement.SetAttribute("searching", user);
        XmlNodeList tests = xmlDoc.SelectNodes("//User[Name =/*/@searching]/Attempts");
        listBox1.Items.Clear();
        listBox1.Items.Add("Runners Name: " + user);
        listBox1.Items.Add("Overall  Distance in Paris: " + run.averageAdd);
        listBox1.Items.Add("Overall Distance in Madrid: " + run.averageSub);

例如,如果我的 xml 文件看起来像这样

    Users>
    <User>
     <Name>David</Name>
      <Attempts>
       <Place>Paris</Place>
       <Date>3/29/2012</Date>
       <Distance>100</Distance>
     </Attempts>
     <Attempts>
      <Place>Madrid</Place>
      <Date>7/28/2012</Date>
      <Distance>100</Distance>
     </Attempts>
     <Attempts>
      <Place>Paris</Place>
      <Date>8/19/2012</Date>
      <Distance>60</Distance>
     </Attempts>
     <Attempts>
      <Place>Madrid</Place>
      <Date>9/29/2012</Date>
      <Distance>200</Distance>
    </Attempts>  
   </User>
   <User>
    <Name>Lenny</Name>
     <Attempts>
      <Place>Paris</Place>
      <Date>9/29/2012</Date>
      <Distance>130</Distance>
     </Attempts>
  </User>
 </Users>

如果我为 david 运行代码,它会打印出类似这样的内容

用户:大卫

巴黎的平均距离://数据

马德里的平均距离://数据

这不是我想要的,我想要的是从文本框中选择任意两个日期并仅显示这两个日期之间的信息 例如,如果我选择大卫,从 2012 年 3 月 29 日到 2012 年 8 月 29 日

我想要并输出如下内容:

用户:大卫

2012 年 3 月 29 日至 2012 年 8 月 29 日在巴黎的平均距离://数据

2012 年 3 月 29 日至 2012 年 8 月 29 日马德里的平均距离://数据

我已经尝试了几个小时,我需要帮助来实现这个

【问题讨论】:

标签: c# xml linq xml-parsing


【解决方案1】:

您可以使用此Xml library 来读取日期并按日期排序。

XElement root = XElement.Load(file);
XElement david = root.XPathElement("//User[Name={0}]", "David");
XElement[] attempts = david
         .XPath("Attempts[Date>={0} and Date<={1}]",
                new DateTime(2012, 3, 29), new DateTime(2012, 8, 29))
         .ToArray();

【讨论】:

    【解决方案2】:

    您可以使用 Linq to Xml 做您需要的事情:

    XElement x = XElement.Load("In.xml");
    IFormatProvider f = new System.Globalization.CultureInfo("en-US");
    
    DateTime bdate = DateTime.Parse("3/29/2012", f);
    DateTime edate = DateTime.Parse("8/29/2012", f);
    string username = "David";
    
    var info = x.Elements("User")
                .Where(u => u.Element("Name").Value == username)
                .Select(u => new
    {
      Name = u.Element("Name").Value,                   //user name
      AverageAttempts = u.Elements("Attempts")          //select user's attempts 
                         .Where(a =>                    //filter by dates
                         {
                            DateTime d = DateTime.Parse(a.Element("Date").Value, f);
                            return d >= bdate && d <= edate;
                         })
                         .GroupBy(a => a.Element("Place").Value) //group by place
                         .Select(g => new         // create summary info by place
                         {
                            Place = g.Key,              //place
                            BeginDate = g.Elements("Date") 
                                         .Select(d => DateTime.Parse(d.Value, f))
                                         .Min(),   //min date, i.e. first attempt
                            EndDate = g.Elements("Date")   
                                       .Select(d => DateTime.Parse(d.Value, f))
                                       .Max(),   //max date, i.e. last attempt
                            Distance = g.Elements("Distance")//average distance
                                        .Average(d => decimal.Parse(d.Value))
                         })
    })
    .FirstOrDefault();
    
    if(info!=null)
    {
       Console.WriteLine(info.Name);
       foreach (var aa in info.AverageAttempts)
       {
           Console.WriteLine(string.Format("{0} [{1} - {2}]:\t{3}",
                                           aa.Place,
                                           aa.BeginDate,
                                           aa.EndDate,
                                           aa.Distance));
       }
    }
    

    输出包含的不是用于过滤的日期,而是用户尝试的实际最小和最大日期。

    当然,此代码不包含对文件中是否存在所有必要的 xml 标记或值是否为有效日期和小数的任何有效性检查...您可以根据自己的需要对其进行修改.

    【讨论】:

    • 谢谢,它可以在 Windows 窗体应用程序上运行吗?我还需要在文本框中为用户提供一个名称,我可以选择我想要的日期,例如开始日期将在文本框中,结束日期将在另一个文本框中。它还应该一次显示一个用户
    • @IsraelRodriguez 当然会。您只需将Console 替换为您的Windows 窗体编辑器...
    • @IsraelRodriguez 我已经稍微修改了我的答案,看看它
    【解决方案3】:

    使用 LINQ to XML,假设您从 TextBox 中选择了两个日期:

    var userElement = xDox.Descendants("User")
                    .SingleOrDefault(u => u.Element("Name").Value == "David");
    
    if (userElement != null)
    {
        var result = userElement.Descendants("Attempts")
            .Select(a => new
                {
                    Place = a.Element("Place").Value,
                    Date = DateTime.Parse(a.Element("Date").Value),
                    Distance = int.Parse(a.Element("Distance").Value)
                })
    
            .Where(a => a.Date >= DateTime.Parse("3/29/2012")
                        && a.Date <= DateTime.Parse("8/29/2012"))
    
            .GroupBy(a => a.Place)
            .Select(g => new {Place = g.Key, Avg = g.Average(x => x.Distance)});
    }
    

    【讨论】:

    • 如何在列表框或文本框中显示它?
    • @IsraelRodriguez:它返回一个列表,循环一个列表以构建字符串以便显示在文本框上,如果你不知道,你应该问另一个问题
    猜你喜欢
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    相关资源
    最近更新 更多