4

I am struggling to wrap my head around xslt... trying to convert the following xml:

<employees>
    <employee>
        <employeeNumber>1234</employeeNumber>
        <startdate>01/02/2003</startdate>
        <activeFlag>true</activeFlag>
        <firstname>Erik</firstname>
        <address>
            <addressline1>123 Main</addressline1>
            <zip>07016</zip>
            <state>New Jersey</state>
            <city>My City</city>
        </address>
    </employee>
</employees>

into this (i.e. taking the activeFlag tag value out and putting it into an attribute of the employee tag instead).

<employees>
    <employee active="true">
            <employeeNumber>1234</employeeNumber>
            <startdate>01/02/2003</startdate>
            <firstname>Erik</firstname>
            <address>
                <addressline1>123 Main</addressline1>
                <zip>07016</zip>
                <state>New Jersey</state>
                <city>My City</city>
            </address>
    </employee>
</employees>

I've tried the following XSLT, but it just does nothing:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="employees/employee">       
        <employee active="{activeFlag}"/>
    </xsl:template>

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

Any ideas?

2 Answers 2

3

This short and simple (no explicit conditional instructions) 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()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="employee">
   <employee active="{activeFlag}">
     <xsl:apply-templates select="node()|@*"/>
   </employee>
 </xsl:template>

 <xsl:template match="activeFlag"/>
</xsl:stylesheet>

when applied on the provided XML document:

<employees>
    <employee>
        <employeeNumber>1234</employeeNumber>
        <startdate>01/02/2003</startdate>
        <activeFlag>true</activeFlag>
        <firstname>Erik</firstname>
        <address>
            <addressline1>123 Main</addressline1>
            <zip>07016</zip>
            <state>New Jersey</state>
            <city>My City</city>
        </address>
    </employee>
</employees>

produces the wanted, correct result:

<employees>
  <employee active="true">
    <employeeNumber>1234</employeeNumber>
    <startdate>01/02/2003</startdate>
    <firstname>Erik</firstname>
    <address>
      <addressline1>123 Main</addressline1>
      <zip>07016</zip>
      <state>New Jersey</state>
      <city>My City</city>
    </address>
  </employee>
</employees>

Explanation: Overriding of the identity rule, use of AVT.

In case you want to handle correctly the cases where no activeFlag child exists, it becomes just slightly more complicated:

 <xsl:template match="employee">
   <employee active=
    "{concat(activeFlag,
             substring('false',
                       1 div not(activeFlag))
             )
      }">
     <xsl:apply-templates select="node()|@*"/>
   </employee>
 </xsl:template>
Sign up to request clarification or add additional context in comments.

3 Comments

ok - thanks both so much... one more follow up question (sorry for not including in the OP). I tried to use this method to also add xmlns, xmlns:xsi, and xsi:schemaLocation attributes to the employees tag - but that errored out. are these special attributes or something?
@ErikSorensen: xmlns= isn't an attribute -- this is a namespace declaration. There isn't any problem creating a namespace declaration if the namespace-uri is statically known -- if not in XSLT 1.0 one needs to use a trick that involves the xxx:node-set() extension -- in order to create a dynamic namespace.
@ErikSorensen: Glad my answer was useful to you. Why not consider accepting it?
3

This XSLT 1.0 Stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <xsl:template match="employee">
    <xsl:copy>
      <xsl:if test="activeFlag">
        <xsl:attribute name="active"><xsl:value-of select="activeFlag"/></xsl:attribute>
      </xsl:if>
      <xsl:apply-templates/>      
    </xsl:copy>
  </xsl:template>

  <xsl:template match="activeFlag"/>

</xsl:stylesheet>

applied to your example XML produces:

<employees>
   <employee active="true">
      <employeeNumber>1234</employeeNumber>
      <startdate>01/02/2003</startdate>
      <firstname>Erik</firstname>
      <address>
         <addressline1>123 Main</addressline1>
         <zip>07016</zip>
         <state>New Jersey</state>
         <city>My City</city>
      </address>
   </employee>
</employees>

You can remove the xsl:if if you're sure that activeFlag will exist or if you don't care if the attribute gets created whether it exists or not.

Also, the reason your stylesheet wasn't working is because you weren't applying templates in your employees/employee match. You could've also used this template:

  <xsl:template match="employees/employee">       
    <employee active="{activeFlag}">
      <xsl:apply-templates/>
    </employee>
  </xsl:template>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.