Reputation: 2347
I'm having trouble inserting a block of text when a certain condition is met into an xml document using XSLT. Suppose I have the following xml:
<oldStep Name="Step1">
<oldItems>
<oldItem ItemName="item1">
</oldItem>
<!-- want to add an extra <Item> here if Step Name == "Step1" -->
<oldItem ItemName="Step1item">
</oldItem>
</oldItems>
</oldStep>
<oldStep Name="Step2">
<oldItems>
...
</oldItems>
</oldStep>
Amongst the conversion of the old node names into new ones, I want to add an extra block of text (or a manually constructed node) whenever oldStep Name is equal to a certain value (in this case, Step1). I tried using the following XSLT, but it ended up with a weird behaviour in adding the block of text to every single node (sometimes even not under a node) in the xml once its matched. Also, I'm having trouble skipping the node so the node can be inserted within Items, not directly under oldStep:
<xsl:template match="oldItems">
<xsl:element name="Item">
<xsl:if test="../@Name = 'Step1'>
<xsl:call-template name = "identity"></xsl:call-template>
</xsl:if>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="node()" name="identity">
<xsl:element name="Item">
<xsl:attribute name="ItemName">Step1item</xsl:attribute>
</xsl:apply-templates />
</xsl:element>
</xsl:template>
I get the feeling that I've gotten the conditions wrong, but googling didn't really help (search string too vague). What am I missing in the xsl? Or, is there a better approach to this problem?
Thanks.
Upvotes: 1
Views: 542
Reputation: 7855
Your description is a little hard to follow. Here's how I interpret your question:
Give me a transformer that copies everything by default, except:
- it converts oldItem elements into Item elements
- it adds an extra item to the oldItems element under "step 1", as the last element
And here's a solution:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- by default, copy everything -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- special handling for old items -->
<xsl:template match="oldItems">
<!-- transform all old items first -->
<xsl:apply-templates select="@*|node()"/>
<xsl:if test="../@Name = 'Step1'">
<xsl:call-template name="add-new-step1-stuff"/>
</xsl:if>
</xsl:template>
<!-- add new step 1 stuff -->
<xsl:template name="add-new-step1-stuff">
<xsl:element name="Item">
<xsl:attribute name="ItemName">Step1item</xsl:attribute>
</xsl:element>
</xsl:template>
<!-- converts each oldItem to Item -->
<xsl:template match="oldItem">
<xsl:element name="Item">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
which, when applied to:
<?xml version="1.0" encoding="utf-8"?>
<oldSteps>
<oldStep Name="Step1">
<oldItems>
<oldItem ItemName="item1"></oldItem>
</oldItems>
</oldStep>
<oldStep Name="Step2">
<oldItems>
<oldItem ItemName="item2"></oldItem>
<oldItem ItemName="item3"></oldItem>
</oldItems>
</oldStep>
</oldSteps>
(not exactly what you gave, but what I think you were going for)
yields:
<?xml version="1.0"?>
<oldSteps>
<oldStep Name="Step1">
<Item ItemName="item1"/>
<Item ItemName="Step1item"/>
</oldStep>
<oldStep Name="Step2">
<Item ItemName="item2"/>
<Item ItemName="item3"/>
</oldStep>
</oldSteps>
which needs some whitespace cleanup, but I think is the structure you're looking for.
If you wanted block text instead of an extra item, you can simply rewrite the add-new-step1-stuff
template like this:
<!-- add new step 1 stuff -->
<xsl:template name="add-new-step1-stuff">
<xsl:text>
Here's some block text that goes at the end.
</xsl:text>
</xsl:template>
Key differences between my stylesheet and yours:
Upvotes: 1
Reputation: 1074
Your template named "identity" matches any node, so will apply it for every node it processes. Don't have it match anything.
Upvotes: 1
Reputation: 243579
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="oldStep[@Name='Step1']/*/oldItem[1]">
<xsl:call-template name="identity"/>
<Item ItemName='Step1item'/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<steps>
<oldStep Name="Step1">
<oldItems>
<oldItem ItemName="item1"/>
<!-- want to add an extra <Item> here if Step Name == "Step1" -->
<oldItem ItemName="Step1item"/>
</oldItems>
</oldStep>
<oldStep Name="Step2">
<oldItems>
...
</oldItems>
</oldStep>
</steps>
produces the wanted result:
<steps>
<oldStep Name="Step1">
<oldItems>
<oldItem ItemName="item1"/>
<Item ItemName="Step1item"/><!-- want to add an extra <Item> here if Step Name == "Step1" -->
<oldItem ItemName="Step1item"/>
</oldItems>
</oldStep>
<oldStep Name="Step2">
<oldItems>
...
</oldItems>
</oldStep>
</steps>
Upvotes: 1