Skip to content

Instantly share code, notes, and snippets.

@nichtich
Last active July 7, 2025 09:03
Show Gist options
  • Save nichtich/dc21e1b1d1664da48eaca0450bc4b647 to your computer and use it in GitHub Desktop.
Save nichtich/dc21e1b1d1664da48eaca0450bc4b647 to your computer and use it in GitHub Desktop.
Add XML processing-instruction to track the location of each XML element as unique XPath expression
<?xml version="1.0" encoding="UTF-8"?>
<!--
Recursively copy XML document (without comments and processing instructions) and
add a processing-instruction "path" with an XPath expression to locate each element.
For instance:
<a>
<b>foo</b>
<b>bar</b>
</a>
becomes
<a><?path /a?>
<b><?path /a/b[1]?>foo</b>
<b><?path /a/b[2]?>bar</b>
</a>
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:param name="base"/>
<xsl:variable name="path">
<xsl:value-of select="$base"/>
<xsl:text>/</xsl:text>
<xsl:value-of select="name()" />
<xsl:if test="count(../*[name() = name(current())]) &gt; 1">
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name() = name(current())]) + 1" />
<xsl:text>]</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:processing-instruction name="path">
<xsl:value-of select="$path"/>
</xsl:processing-instruction>
<xsl:apply-templates select="node()">
<xsl:with-param name="base">
<xsl:value-of select="$path"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|text()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
@nichtich
Copy link
Author

nichtich commented Jul 7, 2025

Use case: given a subset of a full XML document, enriched with processing instructions with the script above:

<b><?path /a/b[1]?>foo</b>

It is possible to get the line numbers of the subset (given the original document) with an event parser such as SAX.

It is possible to show the subset as it is (with processing instruction filtered out)

<b>foo</b>

It is possible to expand from the subset (given the original document)

  • /a/b[1]/parent::*
  • /a/b[1]/following-sibling::*[1]
  • ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment