Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Westi on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Changing node names and inserting nodes with XSLT 2

Status
Not open for further replies.

EwS

Programmer
Dec 30, 2002
398
US
I'm using XSLT to tranform an order create xml into an order response xml. My stylesheet needs to do two things:

1. Replace all occurrences of "OrderCreate" with "OrderResponse". The code below replaces a single tag called <OrderCreate>. How do I make it replace tags like <OrderCreateBody> into <OrderResponseBody>, <OrderCreateDetails> into <OrderResponseDetails>? I would like to use a wild-card character to do that instead of coding <xsl:template match="xxx"> for each of them.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no"/>
  <xsl:template match="/">
    <xsl:apply-templates select="*"/>
  </xsl:template>
  <xsl:template match="node()">
    <xsl:copy>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="OrderCreate">
    <OrderResponse>
      <xsl:apply-templates select="node()"/>
    </OrderResponse>
  </xsl:template>
</xsl:stylesheet>

2. I need to insert an additional ResponseStatus element
Code:
<ResponseStatus>
  <ResponseCode>
  </ResponseCode>
  <ResponseMessage>
  </ResponseMessage>
</ResponseStatus>
right before a certain node. How can I do this?

Thanks a lot!!!
 
[1] Your sample template seems to suggest certain kind abstention of attributes. Base on it, this is how you do the metamorphose of element name deriving from it.
[tt]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="<xsl:eek:utput version="1.0" encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no"/>
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="node()">
<xsl:choose>
<xsl:when test="contains(local-name(),'Create')">
<xsl:variable name="a" select="substring-before(local-name(),'Create')" />
<xsl:variable name="b" select="'Response'" />
<xsl:variable name="c" select="substring-after(local-name(),'Create')" />
<xsl:element name="{$a}{$b}{$c}">
<xsl:apply-templates select="node()"/>
</xsl:element>
</xsl:when>
<xsl:eek:therwise>
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:eek:therwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
[/tt]
[2] As to insert certain document fragment before certain tag,... there is too many "certain"... Principle is to test the name of the element matched, within the template, if the name is what you need to identify, script the document fragement there before starting testing the existence of a "Create" particle within the name as shown above. There I leave it to yourself to fill in the detail.
 
The code worked fine. And the answer for second question gave me some direction.
Thank you very much.
 
For #2, let's say I need to insert <3></3> into the following:
Code:
<X>
  <1></1>
  <2></2>
</X>

Here's how I do it:
Code:
?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no"/>
  <xsl:template match="/">
    <xsl:apply-templates select="*"/>
  </xsl:template>
  
  <xsl:template match="node()">
    <!-- Insert <3></3>. -->
    <xsl:if test="contains(local-name(),'X')">
      <xsl:apply-templates select="node()"/>
      <3></3>
    </xsl:if>
    
    <xsl:choose>
      <!-- Convert all occurrences of 'Create' to 'Response'. -->
      <xsl:when test="contains(local-name(),'Create')">
        <xsl:variable name="a" select="substring-before(local-name(),'Create')" />
        <xsl:variable name="b" select="'Response'" />
        <xsl:variable name="c" select="substring-after(local-name(),'Create')" />
        <xsl:element name="{$a}{$b}{$c}">
          <xsl:apply-templates select="node()"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
 
Actually, the code in the previous post doesn't do what I want. Instead of this:
Code:
<X>
  <1></1>
  <2></2>
  <3></3>
</X>
I get this:
Code:
<3></3>
<X>
  <1></1>
  <2></2>
</X>

How do I insert a child element?
 
This is a bit strange, but try something like this:
Code:
  <xsl:template match="node()">
    <xsl:choose>
      <!-- Convert all occurrences of 'Create' to 'Response'. -->
      <xsl:when test="contains(local-name(),'Create')">
        <xsl:element name="concat(substring-before(local-name(),'Create'),
                           'Response', 
                           substring-after(local-name(),'Create'))">
          <xsl:apply-templates select="node()"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="node()"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>

    <!-- Insert <3></3> as the last child element of X. -->
    <xsl:if test="(position() = last()) and contains(local-name(..),'X')">
      <3></3>
    </xsl:if>
    
  </xsl:template>
[small]Warning: Typed, not tested![/small]
This should give you the idea of determining when you are processing the last (or first) element in a node set, which appears to be what you want...

Tom Morrison
 
I think, Tom, you would find some problem in using xslt function in the mandatory name attribute of xsl:element that way where the use of avt is not replacable with the construction like that - I think I pointed out in one previous thread to show a generic use of avt.
 
Further note:

I understand <1>,<2>,<3> etc are symbolic, just in case op does not know, it is not a correct name in a well-formed xml document, ie, it makes the document malformed by their own. One can easily choose better symbolic name respecting the xml spec of well-formedness.

[tt]
2.3 said:
[Definition: A Name is a token beginning with a letter or one of a few punctuation characters, and continuing with letters, digits, hyphens, underscores, colons, or full stops, together known as name characters.] Names beginning with the string "xml", or with any string which would match (('X'|'x') ('M'|'m') ('L'|'l')), are reserved for standardization in this or future versions of this specification.
[unquote][/tt]
 
tsuji said:
I think, Tom, you would find some problem in using xslt function in the mandatory name attribute of xsl:element

Of course, you are correct. A silly, careless mistake which I regret, and for which I apologize. [blush]

Tom Morrison
 
Really, no apol is needed even as a matter of etiquette. Just want to put a technical matter shines its own light however dime or bright based on its own merit.
 
Thank you very much for all your help! I got it to work like I wanted.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top