XPath string comparisons
-
I spent way too long yesterday discovering that XPath 1.0 does not support string comparisons for anything but equal as the args are automatically converted to number...not much use when you are trying to filter an xml document with a date attribute!...or any string which doesn't convert to number) The weird thing about this is that this was VB6 code I am migrating...meaning, it works fine with ancient XML libs, but not a modern framework. (currently targeting 4.0) Such is progress! :laugh: I guess I'm used to going forward, not backward! :) They may have fixed/added this in XPath 2.0 which I think is available under .NET 4.6.2 or something. I chose another method not involving XPath once I discovered why no results were being returned. It was the hours spent cursing and swearing, not understanding why something that looks like it should work doesn't. No error, just no results. It was easy to understand why once I knew that the rules had changed. For clarity, I'm providing a sample xml doc and code: The sample xml document was taken from MS sample code. I only added the publish_date attribute as this was akin to my actual usage.
The Handmaid's Tale Margaret Atwood 19.95 The Poisonwood Bible Barbara Kingsolver 11.99 The Bean Trees Barbara Kingsolver 5.99
and now, the code:
Dim xmlDoc As New System.Xml.XmlDocument
Dim xmlNodes As System.Xml.XmlNodeList
Dim root As System.Xml.XmlNode
xmlDoc.Load("..\bookstore.xml")
Dim nsmgr As System.Xml.XmlNamespaceManager = New System.Xml.XmlNamespaceManager(xmlDoc.NameTable)
nsmgr.AddNamespace("bk", "urn:newbooks-schema")
root = xmlDoc.DocumentElement
'this returns 1 node/record
xmlNodes = root.SelectNodes("descendant::bk:book[@publish_date = '1901-10-29']", nsmgr)Debug.Print(x
-
I spent way too long yesterday discovering that XPath 1.0 does not support string comparisons for anything but equal as the args are automatically converted to number...not much use when you are trying to filter an xml document with a date attribute!...or any string which doesn't convert to number) The weird thing about this is that this was VB6 code I am migrating...meaning, it works fine with ancient XML libs, but not a modern framework. (currently targeting 4.0) Such is progress! :laugh: I guess I'm used to going forward, not backward! :) They may have fixed/added this in XPath 2.0 which I think is available under .NET 4.6.2 or something. I chose another method not involving XPath once I discovered why no results were being returned. It was the hours spent cursing and swearing, not understanding why something that looks like it should work doesn't. No error, just no results. It was easy to understand why once I knew that the rules had changed. For clarity, I'm providing a sample xml doc and code: The sample xml document was taken from MS sample code. I only added the publish_date attribute as this was akin to my actual usage.
The Handmaid's Tale Margaret Atwood 19.95 The Poisonwood Bible Barbara Kingsolver 11.99 The Bean Trees Barbara Kingsolver 5.99
and now, the code:
Dim xmlDoc As New System.Xml.XmlDocument
Dim xmlNodes As System.Xml.XmlNodeList
Dim root As System.Xml.XmlNode
xmlDoc.Load("..\bookstore.xml")
Dim nsmgr As System.Xml.XmlNamespaceManager = New System.Xml.XmlNamespaceManager(xmlDoc.NameTable)
nsmgr.AddNamespace("bk", "urn:newbooks-schema")
root = xmlDoc.DocumentElement
'this returns 1 node/record
xmlNodes = root.SelectNodes("descendant::bk:book[@publish_date = '1901-10-29']", nsmgr)Debug.Print(x
That doesn't sound right at all. String Functions[^] Here's a demo: XPath String Functions | C# Online Compiler | .NET Fiddle[^] You must be doing something wrong. :laugh:
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
That doesn't sound right at all. String Functions[^] Here's a demo: XPath String Functions | C# Online Compiler | .NET Fiddle[^] You must be doing something wrong. :laugh:
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Richard Deeming wrote:
You must be doing something wrong
:laugh: Without a doubt...I'm taking a big risk here, exposing my ignorance! :laugh: I've edited the OP with a sample for clarity. I really didn't intend to use the forum for QA, but I think maybe I was a little vague on the exact problem. Thanks! :)
"Go forth into the source" - Neal Morse
-
Richard Deeming wrote:
You must be doing something wrong
:laugh: Without a doubt...I'm taking a big risk here, exposing my ignorance! :laugh: I've edited the OP with a sample for clarity. I really didn't intend to use the forum for QA, but I think maybe I was a little vague on the exact problem. Thanks! :)
"Go forth into the source" - Neal Morse
OK, the "less than" and "greater than" operators don't work for strings, as per the specification:
When neither object to be compared is a node-set and the operator is
<=
,<
,>
= or>
, then the objects are compared by converting both objects to numbers and comparing the numbers according to IEEE 754.I'd be very surprised if that wasn't also the case with the old MSXML library. But then again, it was written by 90's Microsoft - they weren't the best at sticking to the specifications! :-D There's no obvious sign of Microsoft implementing XPath 2 or 3 in .NET; there's an open suggestion from 2013[^] which is "under consideration", with no sign of any action. There's also an MSDN blog post from 2004[^] suggesting they would be working on XQuery instead. Again, no sign of any progress on that front. It's also worth pointing out that you can't use the less than or greater than operators on strings in .NET code either. Instead, you have to use the
CompareTo
method. You can get close by using LINQ to XML, but that obviously requires you to load the entire document into memory:var doc = XDocument.Parse(@"<?xml version='1.0'?>
<bookstore xmlns=""urn:newbooks-schema"">
<book genre=""novel"" style=""hardcover"" publish_date=""1825-04-02"">
...
</bookstore>");var nodes = doc.Descendants(doc.Root.Name.Namespace + "book")
.Where(el => ((string)el.Attribute("publish_date")).CompareTo("1901-10-29") >= 0);// Or:
var nodes = doc.Descendants(doc.Root.Name.Namespace + "book")
.Where