我正在尝试创建一个接受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);
}
答案 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);
如果您需要在删除之前记录所有节点,则可以枚举过期的条目。