Ho utilizzato questa tecnica per generare dinamicamente un accordion menù multilivello ...
Consideramo il seguente diagramma ad albero, rappresentazione della struttura di un possibile menù di navigazione:
- Root (nodeID=1, parentID=0)
- Item 1 (nodeID=2, parentID=1)
- item 1.1 (nodeID=3, parentID=2)
- item 1.2 (nodeID=4, parentID=2)
- item 1.2.1 (nodeID=5, parentID=4)
- item 1.3 (nodeID=6, parentID=2)
- Item 2 (nodeID=7, parentID=1)
La seguente struttura può essere memorizzata nella seguente tabella di un database: MenuTable(nodeID,parentID,title).
A partire dalla tabella MenùTable è possibile generare facilmente la rappresentazione XML del grafo ad albero utilizzando il seguente codice:
Dim sqlString As String = "SELECT NodeID, ParentID, title FROM MenuTable ORDER BY NodeID"
Dim cmd As SqlCommand = New SqlCommand(sqlString)
Dim conn As SqlConnection
conn = New SqlConnection(DBConnection)
Dim ds As DataSet = New DataSet
Dim da As SqlDataAdapter = New SqlDataAdapter
da.SelectCommand = cmd
da.Fill(ds)
Dim dt As DataTable = ds.Tables(0)
' create an XmlDocument (with an XML declaration)
Dim XDoc As New XmlDocument()
Dim XDec As XmlDeclaration = XDoc.CreateXmlDeclaration("1.0", Nothing, Nothing)
XDoc.AppendChild(XDec)
' iterate through the sorted data
' and build the XML document
For Each Row As DataRow In dt.Rows
' create an element node to insert
' note: Element names may not have spaces so use ID
' note: Element names may not start with a digit so add underscore
Dim NewNode As XmlElement = XDoc.CreateElement("_" & Row("NodeID").ToString())
NewNode.SetAttribute("NodeID", Row("NodeID").ToString())
NewNode.SetAttribute("ParentID", Row("ParentID").ToString())
NewNode.SetAttribute("title", Row("title").ToString())
' special case for top level node
If IsDBNull(Row("ParentID")) Then
XDoc.AppendChild(NewNode)
Else
' root node
' use XPath to find the parent node in the tree
Dim SearchString As [String]
SearchString = [String].Format("//*[@NodeID=""{0}""] ", Row("ParentID").ToString())
Dim Parent As XmlNode = XDoc.SelectSingleNode(SearchString)
If Parent IsNot Nothing Then
Parent.AppendChild(NewNode)
Else
' Handle Error: Employee with no boss
End If
End If
Next
XDoc.Save(Server.MapPath("~/menu/menu.xml"))
In tal modo nella cartella menu della nostra applicazione verrà generato il file menu.xml così fatto:
<?xml version="1.0"?>
<_1 NodeID="1" ParentID="0" title="Root">
<_2 NodeID="2" ParentID="1" title="item 1">
<_3 NodeID="3" ParentID="2" title="item 1.1" />
<_4 NodeID="4" ParentID="2" title="item 1.2" >
<_5 NodeID="5" ParentID="5" title="item 1.2.1" />
</_4>
<_6 NodeID="6" ParentID="2" title="1.3" />
</_2>
<_7 NodeID="7" ParentID="1" title="item 2"/>
</_1>
Vogliamo ora trasformare la struttura XML in una stringa HML così fatta:
<li><a name="_2" href="#">item 1</a>
<ul>
<li><a name="_3" href="page.aspx?nodeID=3">item 1.1</a></li>
<li><a name="_4" href="#">item 1.2</a>
<ul>
<li><a name="_5" href="page.aspx?nodeID=5">item 1.2.1</a>
</ul>
</li>
<li><a name="_6" href="page.aspx?nodeID=6">item 1.3</a></li>
</ul>
</li>
<li><a name="_7" href="page.aspx?nodeID=7">item 2</a></li>
Definiamo quindi il seguente file XSLT di trasformazione e memorizzaiamolo nella cartella "menu" con il nome menu.xslt
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<!--The <xsl:template> element contains rules to apply when a specified node is matc-->
<xsl:template match="node()">
<!--calcola il numero di nodi figli dell'elemento corrente-->
<xsl:variable name="total-el">
<xsl:value-of select="count(descendant::*)"/>
</xsl:variable>
<xsl:variable name="name">
<xsl:value-of select="name()"/>
</xsl:variable>
<!--se il nodo nonè la root ed il numero di nodi figlio è >0 allora applica il seguente modello ai nodi figlio dell'elemento corrente..-->
<xsl:if test="$total-el > 0 and $name != '_1'">
<li>
<a name="{name()}" href="#" rel="self">
<xsl:value-of select="@NodeHTML"/>
</a>
<ul>
<xsl:apply-templates />
</ul>
</li>
</xsl:if>
<xsl:if test="$total-el > 0 and $name = '_1'">
<ul class="topnav">
<xsl:apply-templates />
</ul>
</xsl:if>
<!--altrimenti ...-->
<xsl:if test="$total-el = 0">
<li>
<a name="{name()}" href="articles.aspx?NodeID={name()}">
<xsl:value-of select="@NodeHTML"/>
</a>
</li>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Non ci resta infine che applicare la trasformazione mediante il segunte codice:
Dim xDoc As New XmlDocument()
xDoc.Load(Server.MapPath("~/menu/menu.xml"))
Dim sw As New System.IO.StringWriter()
Dim xslTrans As New XslCompiledTransform()
xslTrans.Load(Server.MapPath("~/menu/menu.xslt"))
xslTrans.Transform(xDoc.CreateNavigator(), New XsltArgumentList(), sw)
Using writer As System.IO.StreamWriter = System.IO.File.AppendText(HttpRuntime.AppDomainAppPath & "menu\menu.txt")
writer.WriteLine(sw.ToString())
End Using
producendo nel file menu.txt nella cartella menu il codice html desiderato.