Reputation: 19097
Here is a snippet of XML:
<AssignmentHistory Version="210002" DateLastPurged="2020-12-11">
<W20190107>
<Teaching>
<Name Class="1">Brother 1</Name>
<Name Class="2">Brother 2</Name>
<Name Class="3">Brother 3</Name>
</Teaching>
</W20190107>
<W20190114>
<Teaching>
<Name Class="1">Brother 1</Name>
<Name Class="2">Brother 2</Name>
<Name Class="3">Brother 3</Name>
</Teaching>
</W20190114>
</AssignmentHistory>
I realise that my use of WYYYYMMDD
for the element name was not very clever. I designed the XML file when I was only learning XML / XSL and now see it not to be good.
The above is stripped down just to show what I need to show.
I know I can do a path query like this:
XmlDocument doc = new XmlDocument();
doc.Load("file.xml");
XmlNodeList nl = doc.SelectNodes("AssignmentHist/*/Teaching");
...
And it will return all of the Teaching
elements in a XmlNodeList
.
The XML file is sorted in descending date order and what I would like to do is use a query that will select all teaching nodes where the date is greater than a specified value. At the moment I have to:
WYYYYMMDD
)W
YYYY
, MM
& DD
componentsDateTime
object and compare the date of interest against xxxx.Date
.It would be nice if the single select query could put all of those out. But can it be done?
Upvotes: 0
Views: 279
Reputation: 22167
Here is a clean method by using LINQ to XML that is available in .Net since 2007.
It is a one liner with chained logic:
<Teaching>
elementsWhere
clause is going to the parent node, its name is converted to a string, "W" is removed, converted to DateTime data type and compared with a DateTime
parameter dt.c#
void Main()
{
DateTime dt = DateTime.Parse("2019-01-10");
XDocument xdoc = XDocument.Parse(@"<AssignmentHistory Version='210002' DateLastPurged='2020-12-11'>
<W20190107>
<Teaching>
<Name Class='1'>Brother 1</Name>
<Name Class='2'>Brother 2</Name>
<Name Class='3'>Brother 3</Name>
</Teaching>
</W20190107>
<W20190114>
<Teaching>
<Name Class='1'>Brother 4</Name>
<Name Class='2'>Brother 5</Name>
<Name Class='3'>Brother 6</Name>
</Teaching>
</W20190114>
</AssignmentHistory>");
var xelem = xdoc.Descendants("Teaching")
.Where(x => DateTime.ParseExact(x.Parent.Name.ToString().Replace("W", "")
, "yyyyMMdd", CultureInfo.InvariantCulture) > dt);
}
Output
<Teaching>
<Name Class="1">Brother 4</Name>
<Name Class="2">Brother 5</Name>
<Name Class="3">Brother 6</Name>
</Teaching>
Upvotes: 2
Reputation: 74605
I don't think you need to go to any great lengths of manipulation here; your strings thankfully will sort alphamerically as they would numerically (W20200101 is greater than W20191231)..
//AssignmentHistory/*[name() >= 'W20190107']
To be clear, the reason I didn't put /Teaching
on the end is because without it the result from the posted example is:
<W20190107>
<Teaching>
<Name Class="1">Brother 1</Name>
<Name Class="2">Brother 2</Name>
<Name Class="3">Brother 3</Name>
</Teaching>
</W20190107>
<W20190114>
<Teaching>
<Name Class="1">Brother 1</Name>
<Name Class="2">Brother 2</Name>
<Name Class="3">Brother 3</Name>
</Teaching>
</W20190114>
And with it the result is:
<Teaching>
<Name Class="1">Brother 1</Name>
<Name Class="2">Brother 2</Name>
<Name Class="3">Brother 3</Name>
</Teaching>
<Teaching>
<Name Class="1">Brother 1</Name>
<Name Class="2">Brother 2</Name>
<Name Class="3">Brother 3</Name>
</Teaching>
Because this data in the latter example is repeated/these Teaching nodes seemingly have nothing to tell them apart, I figured to leave the W nodes in because they provide identity. If you want just the Teaching nodes, use an xpath of:
//AssignmentHistory/*[name() >= 'W20190107']/Teaching
Upvotes: 2
Reputation: 1001
Thats a nice puzzle,
You could try that, it uses an XPath query and string interpolation (so don't forget the $):
DateTime searchDate = new DateTime(2019,01,14);
XmlDocument doc = new XmlDocument();
doc.Load("file.xml");
XmlNodeList nl = doc.SelectNodes($"//Teaching[xs:date(replace(substring(parent::*/name(), 2),'(\\d{{4}})(\\d{{2}})(\\d{{2}})','$1-$2-$3')) > xs:date('{searchDate:yyyy-dd-MM}')]");
I didn't try it in C# code though, so it might have syntax errors.
Upvotes: 1