C#保留XML文件

时间:2017-10-06 10:23:52

标签: c# xml

我正在尝试创建一个接受x保留天数的函数,结果比较了" date"我的XML文件中的标记,并相应地删除条目。

<?xml version="1.0" encoding="utf-8"?>
<root>
  <OFBM time="13:17" date="06.10.2017" saveName="Unnamed save">
    <folder name="file:///C:/Users/AD/Downloads" />
    <folder name="file:///C:/Users/AD/Desktop/t" />
    <folder name="file:///C:/Users/AD/Dropbox/Development/DelOldX/DelOldX" />
  </OFBM>
  <OFBM time="13:17" date="31.08.2017" saveName="Unnamed save">
    <folder name="file:///C:/Users/AD/Downloads" />
    <folder name="file:///C:/Users/AD/Desktop/t" />
    <folder name="file:///C:/Users/AD/Dropbox/Development/DelOldX/DelOldX" />
  </OFBM>
  <OFBM time="13:17" date="31.08.2017" saveName="Unnamed save">
    <folder name="file:///C:/Users/AD/Downloads" />
    <folder name="file:///C:/Users/AD/Desktop/t" />
    <folder name="file:///C:/Users/AD/Dropbox/Development/DelOldX/DelOldX" />
  </OFBM>
</root>

例如,我的retentionDays值为6,今天是10月6日(06.10),因此应该删除10月1日之前的所有内容。我编写了一个执行此操作的函数,但它删除了date属性,而不是整个元素

我的功能:

        public void CleanXML()
        {
        int days = Int32.Parse(tbRetentionDays.Text);
        DateTime minDate = DateTime.Now.AddDays(-days);
        var root = XElement.Load(pathToXml);
        foreach (XElement el in root.Elements("OFBM"))
        {

            foreach (XAttribute el2 in el.Attributes("date"))
            {
                string rawDate = el2.Value;
                DateTime xmlDate = Convert.ToDateTime(rawDate);

                if (xmlDate < minDate)
                {
                    Console.WriteLine(xmlDate + " lower than " + minDate + " Retention: " + days);
                    el.Remove();
                }
            } 
        }
        root.Save(pathToXml);
    }

3 个答案:

答案 0 :(得分:1)

当您致电el.Remove();时,您正在修改您正在迭代的元素集合。这导致并非所有元素都被访问,因此被删除。一种方法可能是存储需要先删除的元素,一旦完成,删除每个元素。

您的代码需要像这样进行调整:

public void CleanXML(string daysText)
{
   int days = Int32.Parse(daysText);
   DateTime minDate = DateTime.Now.AddDays(-days);
   var root = XElement.Load(pathToXml);
   // keep list of items to be removed
   var remove = new List<XElement>();
   foreach (XElement el in root.Elements("OFBM"))
   {
       foreach (XAttribute el2 in el.Attributes("date"))
       {
           string rawDate = el2.Value;
           DateTime xmlDate = Convert.ToDateTime(rawDate);

           if (xmlDate < minDate)
           {
               Console.WriteLine(xmlDate + " lower than " + minDate + " Retention: " + days);
               // keep a reference to this element
               remove.Add(el);
           }
       } 
    }
    // remove individual elements
    foreach(var element in remove)
    {
      element.Remove();
    }
    root.Save(pathToXml);
}

如果您不想拥有该显式列表,可以稍微重写Linq查询,以便获取要删除的元素列表并实现该列表。在这种情况下,主迭代器看起来像这样:

foreach (XElement el in root
    .Elements("OFBM")
    .Where(elem => elem.Attribute("date") != null
                && Convert.ToDateTime(elem.Attribute("date").Value) < minDate
    ).ToList()) // the ToList is mandatory here
    {
       el.Remove();
    }

答案 1 :(得分:0)

看起来存在一个本地化问题,其中第一个数字是月份而不是白天。其阅读时间为06.10.2017,截至6月10日。尝试使用DateTime.ParseExact(rawDate,&#34; dd.MM.yyyy&#34;)代替。这样,您就不必担心代码运行的机器的区域设置。

答案 2 :(得分:0)

您应该检查天文本是否可以解析为整数

int retentionInDays;
if (!Int32.TryParse(daysText, out retentionInDays))
   return; // log, throw etc

因此,您不必检查xml中的时间属性,然后您可以使用DateTime.Today。但我建议你检查xml文件中的日期和时间。

var minDate = DateTime.Today.AddDays(-retentionInDays);
var xdoc = XDocument.Load(pathToXml);
var provider = CultureInfo.InvariantCulture   

接下来,您只需选择过期的节点并将其全部删除。正如@rene注意到的,如果你在foreach中枚举期间删除节点,那么枚举就完成了(虽然我想知道为什么我们这里没有像CollectionModified异常这样的东西)。请注意,您应该手动解析日期,因为xml日期字符串没有默认格式 - 默认情况下,月份在日期之前,"06.10.2017"将转换为6月10日而不是10月6日。 "31.08.2017"将生成FormatException。因此,请在此处使用ParseExact

var expiredEntries = 
   from e in xdoc.Root.Elements("OFBM")
   let date = DateTime.ParseExact((string)e.Attribute("date"), @"dd\.MM\.yyyy", provider)
   where date < minDate
   select e;

expiredEntries.Remove(); // removes all selected nodes
xdoc.Save(pathToXml);

如果您需要在删除之前记录所有节点,则可以枚举过期的条目。