11 June 2009

Use VB 9 XML Literals to parse an XML tree of unknown depth

I’m in the process of building an editor interface for a Flex program we’ve written at my work. It’s my first real project using LINQ. I’ve mainly been using C# but decided to take advantage of XML Literals in VB 9.

The structure of the Flex program is held in an XML file. I’ve been needing to read this XML file in such that the whole navigation structure can be held in a tree control. This is different to the Flex program itself which only needs to display part of the navigation tree at once.

To do this, I needed to transform the tree. As I saw it, I had two options: XSLT (yuck) or LINQ.

The XML doc is structured like the example below.

<sections>   
<section>
<id>section1</id>
<label>This is section 1</label>
<layout>navStructure</layout>
<properties>
<tree>
<!-- Nested collection of node elements -->
<node label="Blah">
<node label="Blah 1" url="somedoc.pdf"/>
<node label="Blah 2" url="somedoc2.pdf"/>
</node>
</tree>
</properties>
</section>
<!-- More section elements -->
</sections>

There are several <section> elements with this layout. The <node> elements also have variable nesting levels.

The hard part is in writing a LINQ query that will find all the <node> elements without knowing the nesting depth beforehand.

This first section of VB is in the calling Sub. Note in the second expression hole that calls out to the BuildChild function.

The BuildChild function takes an XElement as a paramater and writes a <node> element with the label and url attributes. An expression hole is then opened which recurively calls back into the BuildChild function on the collection of XElements contained in the current XElement.

Dim doc As XDocument = XDocument.Load("content.xml")
Dim tree = _

<tree>
<%= From n In doc.<module>.<sections>.<section> _
Where n.<layout>.Value = "navStructure" _
Select <node label=<%= n.<label>.Value %> url="">
<%= From m In n.<properties>.<tree>.<node> _
Select BuildChild(m) _
%>
</node> _
%>
</tree>
Private Function BuildChild(ByVal element As XElement) _
As XElement
Dim retElement As XElement

retElement = <node label=<%= element.@label %> _
url=<%= element.@url %>>
<%= From e In _
element.Elements
Select BuildChild(e) %>
</node>

Return retElement
End Function

This is how I’ve solved my problem for now. My understanding is that LINQ is more closely related to functional programming in its feel and I suspect that it can provide a better solution. Recursion is difficult to read and debug and it would be great to get rid of it if possible.

Cheers
Mike

No comments:

Post a Comment