XPath and XML
My first serious use of XPath came along only relatively recently when
one considers the lifespan of halfgk.com. I had created a schema for
use in a nerdy datagrid of software experience. The schema calls for
two elements that contain elements -- one for platforms and one for
languages. The concept is simple enough: the platforms node can contain
multiple "platform" nodes (operating system platforms, like Windows 10,
Apple OSX and the like), and the "languages" node can likewise contain
multiple "language" nodes (programming languages, like Visual Basic. NET,
JavaScript, and so forth).
Some software runs on multiple platforms, while some are Web-based
systems and run independently of operating systems. Some software
supports multiple programming languages, while others aren't
programming software at all.
I needed a way to list the platforms and languages in a datagrid all
in a single row of a datagrid.
If the schema looks like this:
. . .
I want to display it on a single row of the datagrid, like this:
NAME TYPE PLATFORMS LANGUAGES
=================================================================================
EditPad Pro Text Editor Microsoft Windows CSS, HTML, JavaScript,
Regular Expressions, XML
---------------------------------------------------------------------------------
I don't intend to get too deep into the mechanics of the datagrid
control here -- the purpose of this how-to is to walk you through
using XPath -- though there are places where the two topics
converge. I'll try to keep such unions are brief as possible.
============
CODE FORWARD
============
About the only thing I want to say about the code forward is that
the datagrid columns for the platforms and languages nodes are the
plural nodes ("languages") -- not for the singular ones ("language"),
as below:
The secret sauce here is in the lblLanguages label in the item template of
the "Languages" template column. Or at least, it's where the secret sauce
goes. Also important is the lblName label in the "Name" template column,
because that's the value we're going to use to specify which languages we
want.
Read on.
===========
CODE BEHIND
===========
We're going to skip over most of the contents of the code behind
pertaining to the datagrid -- suffice it to say, the datagrid is
configured and bound to the dataset. But the datagrid will require
an ItemDataBound event handler, because that's where our XPath
goop comes into play.
Essentially, each data item in each row will get populated by the standard
data binding method, and then the ItemDataBound event handler will use
XPath to mine the dataset for the individual platform and language data.
We're going to do that in part by creating a function to handle the
heavy lifting, then call that function from the ItemDataBound event
handler.
Here's our heavy lifter. We're going to pass in the name of a software
and the path of the node we're interested in, and use navigator and
iterator objects to find the goop we're looking for in the XML file
called "software.xml":
Private Function getXpathData (ByVal strName As String, ByVal strPath As String) As ArrayList
Dim listOut As New ArrayList
If Not (strName = String.Empty) AndAlso Not (strPath = String.Empty) Then
Dim doc As New XPathDocument(Server.MapPath("~/software.xml"))
Dim objNavigator As XPathNavigator = doc.CreateNavigator()
Dim objIterator As XPathNodeIterator = objNavigator.Select("/toolset/tool[name = '" & strName & "']" & strPath)
While objIterator.MoveNext()
listOut.Add(objIterator.Current.Value)
End While
'sort the arraylist
listOut.Sort()
End If 'Not (strName = String.Empty) AndAlso Not (strPath = String.Empty)
Return listOut
End Function
Take a good long look at the line where the iterator object is instantiated, and
compare it with the schema near the top of the file. We're telling the iterator
to look at the specific node the navigator can select with the path "/toolset/
tool[name = 'foo']", plus whatever other path information we choose to append
(in the case of the language data, we'd pass in "/languages/language"). Compare
with the schema:
If this data is found, the iterator's job is to add every language node it finds
for software "foo" to the ArrayList object. Next, we're going to sort that list
and return it back to the ItemDataBound event handler.
The code in the ItemDataBound event handler that calls our function is as
follows:
If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
Try
'strName is used to feed the XPath queries
Dim lblName As Label = CType(e.Item.FindControl("lblName"), Label)
Dim strName As String = lblName.Text.Trim
Dim lblLanguages As Label = CType(e.Item.FindControl("lblLanguages"), Label)
. . .
Dim listLanguages As ArrayList = getXpathData(strName, "/languages/language")
For intCount = 0 To (listLanguages.Count - 1)
lblLanguages.Text += CType(listLanguages(intCount), String)
If intCount < (listLanguages.Count - 1) Then
lblLanguages.Text += ", "
End If
Next
. . .
End If
Let's walk this through.
That "If" is a BIG "If" -- without it this code will blow up every time you try
to run it, because the first row the event handler will attempt to evaluate will
be a header row, with none of this code in it.
Inside the Try-Catch-End Try block (only the "Try" appears), our first order of
business is to get the name of the software in the row being examined. We do that
by grabbing the label called "lblName" from the "Name" column in the code forward,
and passing its value along with the path for the languages we want into the
function. (If we were looking for the platform information instead, we'd pass in
the platform path instead.)
We're also getting a reference to the lblLanguages label control for reasons that
will become clear shortly.
As we've already discovered, the function will return for us a neatly sorted
ArrayList object containing all of the languages found for software "foo." The
For-Next loop simply iterates through the list and adds each value to the Text
property of the Languages label ("lblLanguages") which, as stated above, is
the label in the item template of the "Languages" data column. See? I told you
it's where the secret sauce goes!
When the page is loaded, the datagrid is populated, and as each object is data
bound, the ItemDataBound event handler examines each item by type, calls our
XPath function, finds data using the software name and the language path and
returns it to us so we can slap it all in a label. Whew -- that was a long
sentence. The same is true for the platform data elsewhere in the event
handler. Once all of the items are data bound, the completed grid is displayed.
Contact me using the site's contact form if you have questions.
Feel free to use the code in your projects. A shout out in your project would be
thoughtful. Also, drop me a line and let me know how you might have tweaked things
to better suit your needs. Finally, I wouldn't profess to be THE expert on matters
represented in my code -- so drop me a line if you have constructive suggestions,
too. I'd like to hear from you!
Best,
halfgk
copyright 2018 halfgk.com