XML

Sometimes, you want to get the XPath of an element. Here are two working solutions, one in pure XSLT and one in C#.
Both of them return XPath in the form of: /html[1]/body[1]/div[1]/div[2]/div[3]/div[1]/p[2]/img[1]

C#

/// <summary>
/// Gets the X-Path to a given Node
/// </summary>
/// <param name="node">The Node to get the X-Path from</param>
/// <returns>The X-Path of the Node</returns>
public string GetXPathToNode(XmlNode node)
{
	if (node.NodeType == XmlNodeType.Attribute)
	{
		// attributes have an OwnerElement, not a ParentNode; also they have             
		// to be matched by name, not found by position             
		return String.Format("{0}/@{1}", GetXPathToNode(((XmlAttribute)node).OwnerElement), node.Name);
	}
	if (node.ParentNode == null)
	{
		// the only node with no parent is the root node, which has no path
		return "";
	}

	// Get the Index
	int indexInParent = 1;
	XmlNode siblingNode = node.PreviousSibling;
	// Loop thru all Siblings
	while (siblingNode != null)
	{
		// Increase the Index if the Sibling has the same Name
		if (siblingNode.Name == node.Name)
		{
			indexInParent++;
		}
		siblingNode = siblingNode.PreviousSibling;
	}

	// the path to a node is the path to its parent, plus "/node()[n]", where n is its position among its siblings.         
	return String.Format("{0}/{1}[{2}]", GetXPathToNode(node.ParentNode), node.Name, indexInParent);
}

XSLT

  <xsl:template name="GeneratePath">
	<xsl:param name="node" select="." />
	<xsl:message terminate="no">
	  <xsl:copy-of select="$node"/>
	</xsl:message>
	<xsl:apply-templates select="$node" mode="StartGeneratePath" />
  </xsl:template>

  <xsl:template match="*" mode="StartGeneratePath">
	<xsl:call-template name="genPath" />
  </xsl:template>
  
  <xsl:template name="genPath">
	<xsl:param name="prevPath"/>
	<xsl:variable name="currPath" select="concat('/',name(),'[',count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
	<xsl:for-each select="parent::*">
	  <xsl:call-template name="genPath">
		<xsl:with-param name="prevPath" select="$currPath"/>
	  </xsl:call-template>
	</xsl:for-each>
	<xsl:if test="not(parent::*)">
	  <xsl:value-of select="$currPath"/>
	</xsl:if>
  </xsl:template>

XSLT-Usage

<xsl:variable name="path">
  <xsl:call-template name="GeneratePath">
	<xsl:with-param name="node" select="./div[@class='eintrag2']//img[1]"/>
  </xsl:call-template>
</xsl:variable>

I like XML. I like it a lot. For most smaller applications a database – even SQLite and the likes – is complete overkill. Still, you have to save your application data somewhere. Enter XML.

I also like to access my application data in a typesafe manner and I don’t like to reinvent the wheel (or in this case, the XML serialization). Enter System.Xml.Serialization. Continue reading System.Xml.Serialization