Navigating XML problems

Phix

Member
Joined
Apr 9, 2010
Messages
15
Location
San Jose
Programming Experience
3-5
Is it me, or is XML navigation in VB a little more convoluted than it "needs" to be?

I was a little excited to see that XPath was a part of the VB toolbox, but I'm a little confused as to why I'm getting the results that I am.

My xml file:

VB.NET:
<?xml version="1.0"?>
<cfg>
	<basedir>E:\wamp\www</basedir>
	<initial>
		<primDir>
			<dir>assets</dir>
			<dir>static</dir>
			<dir>images</dir>
		</primDir>
		<subDir>
			<dir parent="assets">header</dir>
			<dir parent="assets">links</dir>
			<dir parent="static">js</dir>
			<dir parent="static">css</dir>
			<dir parent="images">PSD</dir>
		</subDir>
		<files>
			<file>index.php</file>
			<file parent="static">color.css</file>
			<file parent="static">layout.css</file>
			<file parent="static">type.css</file>
		</files>
	</initial>
</cfg>

And what I'd like to do is on program startup, get the basedir, store it, get each node in primDir, subDir, and files and store in a List.

Relevant VB code:

VB.NET:
Dim config As New XPathDocument(CFG)
Dim nav As XPathNavigator
Dim ni As XPathNodeIterator

nav = config.CreateNavigator()

ni = nav.Select("basedir")
Debug.WriteLine(ni.Current.Value)

The output for above:

E:\wamp\wwwassetsstaticimagesheaderlinksjscssPSDindex.phpcolor.csslayout.csstype.css

(which is clearly everything in the xml file, whereas I just want "E:...")


It seems a lot of the XML articles out there are either "iterate ALL nodes" or a repeat of another site, so it's becoming difficult to find different examples to extrapolate from.

I'm just not familiar with how vb.net works just yet, and this is just head scratcher.
 
Try changing the last part of your code to this and you'll just get the 'E:...' part you're looking for.

VB.NET:
        Dim ni As XPathNodeIterator = nav.Select("cfg/basedir")
        ni.MoveNext()
        MessageBox.Show(ni.Current.InnerXml)

I would recommend using LINQ to navigate XML files as it makes things much easier. I'll write up a quick example and post it here in a couple of minutes.
 
Decided to go with a pretty involved demonstration to show how you can use LINQ to chain create anonymous types.

I decided to keep the anonymous type following the same layout as the XML file so cfgData has basedir and initial under it and initial has primDirs, subDirs and files under it. Since you won't be using initial you could remove that from the code and have basedir, primDirs, subDirs and files directly under cfgData.

MessageBoxes at the bottom show how you've got a strongly typed object you can enumerate over to get at the data.

This may be a little overwhelming to look at in one go. When building them yourself just add 1 element at a time and test your results after each addition.

VB.NET:
        Dim xdoc As XDocument = XDocument.Parse(IO.File.ReadAllText("C:\Temp\Config.xml"))
        Dim cfgData = (From cfg In xdoc.<cfg> _
                      Select New With {
                          .basedir = cfg.<basedir>.Value, _
                          .initial = New With { _
                              .primDirs = (From pd In cfg...<primDir>.<dir> _
                                           Select New With { _
                                               .dir = pd.Value _
                                           }), _
                              .subDirs = (From sd In cfg...<subDir>.<dir> _
                                          Select New With { _
                                              .parent = sd.Attribute("parent").Value, _
                                              .dir = sd.Value _
                                          }), _
                              .files = (From f In cfg...<files>.<file> _
                                        Select New With { _
                                            .parent = If(f.HasAttributes = False, "", f.Attribute("parent").Value), _
                                            .file = f.Value _
                                        }) _
                            } _
                      }).FirstOrDefault

        'Display the <cfg>.<basedir> value
        MessageBox.Show("basedir=" & cfgData.basedir)

        'Display the <cfg>.<primDir>.<dir> values
        For Each pd In cfgData.initial.primDirs
            MessageBox.Show("primDir=" & pd.dir)
        Next

        'Display the <cfg>.<subDir>.<dir> values and attributes
        For Each sd In cfgData.initial.subDirs
            MessageBox.Show("Dir=" & sd.dir & " Parent=" & sd.parent)
        Next

        'Display the <cfg>.<files>.<file> values and attributes
        For Each f In cfgData.initial.files
            MessageBox.Show("File=" & f.file & " Parent=" & f.parent)
        Next
 
Makes sense. May take a few goes to be able to recreate something like it, but I can see how it's a lot more straightforward than the XPath approach.

Thank you for taking the time to do so. :)
 
Makes sense. May take a few goes to be able to recreate something like it, but I can see how it's a lot more straightforward than the XPath approach.

Thank you for taking the time to do so. :)

You're welcome. I have no problem working up a solution for people that are courteous and have made a legitimate go of it themselves.

The LINQ example was just an example of how I would tackle the situation. Having a nice object you can work with as an end result is worth the extra initial effort. In this case I only took it as far as creating an anonymous type but you could create a typed class and do the same thing.

I would stress adding 1 thing at a time so if something does break you know where it happened and indenting everything for legibility.
 
Thanks again Matt. I'm taking a look at some LINQ examples and walkthroughs and it's not so scary ;)

The one thing I'm starting to see is that for every problem to be solved in vb there's about 12 ways of solving it. Thanks for the push.
 
Back
Top