-2

A question about Martin's answer: Martin Honnen's answer works great, but not with the root element. Let's say I have "cars" as a root element:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="foo">
        <cars>
            <car1 />
            <car2 />
        </cars>
    </xsl:template>
</xsl:stylesheet>

And I want to obtain:

<xsl:template match="foo">
    <cars>
        <car1 />
        <car2 />
        <TANK />
    </cars>
</xsl:template>

For this, I'd use:

<xsl:template match="cars" >
  <xsl:copy>
    <xsl:apply-templates/>
    <TANK />
  </xsl:copy>
</xsl:template> 

Which outputs the exact input, without changing anything. I can try:

<xsl:template match="/" >
  <xsl:copy>
    <xsl:apply-templates/>
    <TANK />
  </xsl:copy>
</xsl:template> 

But it will place the TANK node outside the stylesheet, like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="order">
    <cars>
        <car1/>
        <car2/>
    </cars>
</xsl:template>
</xsl:stylesheet><TANK/>

How to get the TANK element inside cars?

Original question: I have a XSL that I use to transform an XML:

XML_format1 -> XSL1 -> XML_format2

I need to transform this first XSL file (using a second XSL) to obtain a third XSL file, which will output an XML with a third format. In short:

XSL1 -> XSL2 -> XSL3

XML_format1 -> XSL3 -> XML_format3

Using the following stylesheet, I am able to copy the first XSL's contents and also skip certain nodes:

<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="//skipThisNode"/>

</xsl:stylesheet>

My problem: in addition to this, I also need to change some nodes' structure (add something), from this:

<car>
    <color>green</color>
    <fuel>petrol</fuel>
</car>

To this:

<car>
    <color>green</color>
    <fuel>petrol</fuel>
    <topSpeed>99</topSpeed>
</car>

LE: I could create a template to match the specific nodes that I need to add children to, like so:

<xsl:template match="car">
    <color>
        <!-- existing value-of -->  
    </color>
    <fuel>
         <!-- existing value-of --> 
    </fuel>
    <topSpeed>
        <!-- * new value-of * -->  
    </topSpeed>
</xsl:template>

But this seems like going over the top. Is there a simpler way of achieving what I want?

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
Buffalo
  • 3,861
  • 8
  • 44
  • 69

1 Answers1

4

I would rather use

<xsl:param name="newSpeed" select="99"/>

<xsl:template match="car">
  <xsl:copy>
    <xsl:apply-templates/>
    <topSpeed>
      <xsl:value-of select="$newSpeed"/>
    </topSpeed>
  </xsl:copy>
</xsl:template>

that keeps the processing chain up, allowing you to add templates to transform or delete child and descendants of car elements when needed.

[edit] I am not sure I understand your latest requirement as the input seems to be a complete stylesheet but then as the wanted output you have only shown a single template. So assuming you have the input as

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="order">
        <cars>
            <car1 />
            <car2 />
        </cars>
    </xsl:template>
</xsl:stylesheet>

and you want to both transform the xsl:template's match attribute as well as the literal result elements in the template body I would use

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="xsl:template[@match = 'order']/@match">
    <xsl:attribute name="{name()}">
      <xsl:text>foo</xsl:text>
    </xsl:attribute>
  </xsl:template>

  <xsl:template match="xsl:template[@match = 'order']/cars">
    <xsl:copy>
      <xsl:apply-templates/>
      <TANK/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

that way I get (tested with Saxon 6.5.5) the result

<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="foo">
        <cars>
            <car1/>
            <car2/>
        <TANK/></cars>
    </xsl:template>
</xsl:stylesheet>

which is hopefully what you want (lacking proper indentation perhaps).

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110