views:

140

answers:

4

I want to match a root Element “FOO” and perform the transformation (add a version attribute) to it leaving the rest as it is. The Transformation I have so far looks like this:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://schemas.foo.com/fooNameSpace"&gt;

<xsl:template match="//FOO">
    <xsl:choose>
      <xsl:when test="@version">
        <xsl:apply-templates select="node()|@*" />
      </xsl:when>
      <xsl:otherwise>
        <FOO>
         <xsl:attribute name="version">1</xsl:attribute>
         <xsl:apply-templates select="node()|@*" />
        </FOO>
      </xsl:otherwise>
    </xsl:choose>
 </xsl:template>

However this does not perform any transformation. It doesn't even detect the element. So I need to do add the namespace in order to make it work:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fd="http://schemas.foo.com/fooNameSpace"&gt;

 <xsl:template match="//fd:FOO">
 …

But this attaches a namespace attribute to the FOO element as well as other elements:

<FOO xmlns:fd="http://schemas.foo.com/fooNameSpace" version="1" id="fooid">
<BAR xmlns="http://schemas.foo.com/fooNameSpace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
  • Is there a way to say that the element is using the default namespace?
  • Can we match and add elements in the default name space?

Here is the original XML:

  <?xml version="1.0" encoding="UTF-8"?>
  <FOO xmlns="http://schemas.foo.com/fooNameSpace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
      <BAR>
        <Attribute name="HEIGHT">2067</Attribute>
      </BAR>
  </FOO>
A: 

This is similar to this question.

*Edit: Added actual answer instead of a link.

What I would do is similar to others below, but slightly simplified.

Here is the input:

<?xml version="1.0" encoding="UTF-8"?>
<FOO xmlns="http://schemas.foo.com/fooNameSpace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
   <BAR>
      <Attribute name="HEIGHT">2067</Attribute>
   </BAR>
</FOO>

Here is the stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:fd="http://schemas.foo.com/fooNameSpace" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output indent="yes" method="xml"/>
   <xsl:strip-space elements="*"/>

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

   <xsl:template match="fd:FOO[not(@version)]">      
      <xsl:copy>
         <xsl:attribute name="version">1</xsl:attribute>
         <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

Here is the output:

<?xml version="1.0" encoding="utf-8"?>
<FOO xmlns="http://schemas.foo.com/fooNameSpace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1">
   <BAR>
      <Attribute name="HEIGHT">2067</Attribute>
   </BAR>
</FOO>

The only difference between the input and the output is the version attribute. Also notice that I didn't modify the namespace in the original input XML; I only added the :fd to the namespace in xsl:stylesheet.

DevNull
Downvote for...?
DevNull
A: 

This works for me using xsltproc/libxslt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://schemas.foo.com/fooNameSpace"&gt;
  <xsl:template match="/ns:FOO">
    <xsl:copy>
      <xsl:if test="not(@version)">
        <xsl:attribute name="version">1</xsl:attribute>
      </xsl:if>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

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

It produces:

<?xml version="1.0"?>
<FOO xmlns="http://schemas.foo.com/fooNameSpace" version="1">
    <BAR>
        <Attribute>2067</Attribute>
    </BAR>
</FOO>
bkail
Downvote with no suggestion for improvement?
bkail
A: 

Here is one solution truly in the spirit of XSLT:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fd="http://schemas.foo.com/fooNameSpace"
    >
    <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="/*[self::fd:FOO and not(@version)]">
  <xsl:copy>
    <xsl:attribute name="version">1</xsl:attribute>

    <xsl:call-template name="identity"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document, the wanted, correct result is produced:

<FOO xmlns="http://schemas.foo.com/fooNameSpace"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 version="1">
   <FOO>
      <BAR>
         <Attribute name="HEIGHT">2067</Attribute>
      </BAR>
   </FOO>
</FOO>
Dimitre Novatchev
+1  A: 

You can specify the default namespace for XPath expressions by adding the attribute xpath-default-namespace, as described in the section 5.2 Unprefixed QNames in Expressions and Patterns of the XSLT 2.0 standard.

Example:

<xsl:stylesheet version="2.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xpath-default-namespace="http://schemas.foo.com/fooNameSpace"&gt; 

    <xsl:template match="FOO[not(@version)]">
        <xsl:copy>
            <xsl:attribute name="version">1</xsl:attribute> 
            <xsl:apply-templates select="node()|@*" /> 
        </xsl:copy>
    </xsl:template>

    <!-- Identity template for copying everything else -->
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" /> 
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
markusk
I finished the edit to my original answer and then saw your answer; almost identical. +1 for the `xpath-default-namespace`
DevNull