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>