问题
I am trying to find out the position of a child element with respect to its parent.
I have the following input XML:
<Begin>
<tag1>g</tag1>
<tag2>b</tag2>
<tag3>c</tag3>
<tag5>e</tag5>
</Begin>
I need to know the position of <tag3>
with respect to <Begin>
, i.e. 3
Can this be done in XSLT?
My current XSLT looks like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Begin[count(tag4)=0]">
<xsl:copy>
<!-- Need to find the position of /Begin/tag3" here -->
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Basically, what I am trying to do is to find the position of <tag3>
in the input XML
and insert <tag4>
exactly after <tag3>
using the position.
So my question is:
How can I insert an element <tag4>
at the position between <tag3>
and <tag5>
?
This is my intent!
回答1:
An easy way to insert a new element sorted alphabetically is to use <xsl:sort>
in the identity template like this:
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*">
<xsl:sort select="local-name()" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
This modification does copy all elements sorted alphabetically by the local-name/tag-name/element-name.
回答2:
You can find out the position of the child using either xsl:number
, or count(preceding-sibling::*)
.
But that doesn't help you with the next part of the problem: insert <tag4> exactly after <tag3> using the position
. In XSLT you always add new data to the current writing position in the result tree, you can't add it in a specific position. So I think we need to know what you actually want to achieve, rather than your proposed method of achieving it.
回答3:
You do not need to know the position of a node in order to insert another node after (or before) it. In your given example, you can use:
XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tag3">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
<tag4>d</tag4>
</xsl:template>
</xsl:stylesheet>
to return:
<?xml version="1.0" encoding="UTF-8"?>
<Begin>
<tag1>g</tag1>
<tag2>b</tag2>
<tag3>c</tag3>
<tag4>d</tag4>
<tag5>e</tag5>
</Begin>
Of course, if there is no tag3
element in the provided input, then nothing will happen. And if there are several, the new element will be inserted after each one.
回答4:
Like mentioned in my comment, and very similar to @michael.hor257k's answer, instead of checking the position, just change your match to Begin[not(tag4)]/tag3
and then output both tag3
and the new tag4
...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Begin[not(tag4)]/tag3">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
<tag4>d</tag4>
</xsl:template>
</xsl:stylesheet>
Hi @DanielHaley- :) that actually works. But just curious.. how can we find the positon of tag3 w.r.t Begin?
If you were using XSLT 2.0, you could just use <xsl:number select="tag3" count="*"/>
(1.0 doesn't allow the select
element on xsl:number).
In 1.0, you can use a moded template that uses either xsl:number
or count(preceding-sibling::*)
(as suggested by @michael-kay)...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Begin[not(tag4)]">
<xsl:copy>
<!-- Need to find the position of /Begin/tag3" here -->
<xsl:apply-templates select="tag3" mode="getPos"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="getPos">
<xsl:number count="*"/>
<!-- alternative to xsl:number:
<xsl:value-of select="count(preceding-sibling::*) + 1"/>
-->
</xsl:template>
</xsl:stylesheet>
The output would be:
<Begin>3</Begin>
来源:https://stackoverflow.com/questions/43359395/xslt-1-0-how-to-insert-an-element-at-a-specific-position-with-respect-to-the-pa