Reputation: 6062
I need to modify the attribute of an XML node, based on an attribute of it's parent node.
I think it will be better explained with an example...so:
I have an xml file that looks like this:
<pdf2xml>
<page number="1">
<text top="100" />
</page>
<page number="2">
<text top="100" />
<text top="50" />
</page>
</pdf2xml>
I'm currently ordering the text nodes by 'top' within each page with the following xslt template:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<xsl:template match="pdf2xml/page">
<xsl:copy>
<xsl:for-each select=".">
<xsl:apply-templates select="*">
<xsl:sort select="@top" data-type="number"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, I later refer to all the text nodes as one long array using getElementsByTagName("text")
- so what I want to do, is make text@[top] = 5000 * page@[number]
- so that all my text top
attributes are sequential through the whole document.
I've had a look at this question, which helped, but I'm struggling to see how (if I even can?) apply that to a dynamic value, dependent on the position in the hierachy of each text node.
Upvotes: 1
Views: 166
Reputation: 122414
I'd use a template matching the top
attribute itself, from where you can refer to the page number as ../../@number
(in XPath an attribute node is not a child of its containing element, but the containing element is the parent of the attribute node):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<xsl:template match="pdf2xml/page">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="*">
<xsl:sort select="@top" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="text/@top">
<xsl:attribute name="top">
<xsl:value-of select=". + (../../@number * 5000)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
You might prefer to make things more explicit by using ancestor::page[1]/@number
instead of ../../@number
to make it clearer to future readers of the code that it's the nearest containing page
you're trying to reach.
Upvotes: 3