views:

29

answers:

3

Hi,

I'm pretty new to XSLT and have an urgent work requirement that I'm finding quite complex to sort out.

I have an XML doc which needs an attribute adding under certain conditions.

The XML doc is pretty straightforward:

<A x="foo" y="bar" z="">
<B/>
<C/></A>

Basically if attribute "z" is present. Then a new attribute needs to be added to node "A". The value of the attribute needs to be a text string with the values of "x" and "y" substituted at certain places. The result should look like:

<A x="foo" y="bar" z="" new="values present are x=foo and y=bar">
<B/>
<C/></A>

I've gotten as far as creating an XSLT that will copy the document to the attribute level. But I'm stumbling when trying to create the logic that tests for attribute z and creates a string based on x and y.

Can anyone help?

Thanks

Also - apologies if my code formatting sucks

From comments:

There's something that's stopping these solutions working. The text XML I've put above actually has a root node <R> that contains it all. The R node has an attribute like this : xmlns="http://www.fixprotocol.org/FIXML-4-4". Adding this attribute for some reason causes the template matching "A" to not work?!

+1  A: 

You can use the following XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="A">
    <A>
      <xsl:if test="@z">
        <xsl:attribute name="new">
         <xsl:value-of select="concat('values present are x=',@x,' and y=',@y)"/>
        </xsl:attribute>
      </xsl:if>
      <xsl:apply-templates select="@* | node()"/>
    </A>

  </xsl:template>
</xsl:stylesheet>
stapeluberlauf
thank you! that's awesome. I just have one tweak... node A is not actually the root.. it's contained in the root node (let's say "R"). It only works if I take A out and make it the root node as in the example above. I've tried changing the match="A" to match="/R/A" but to no effect. What am I doing wrong?
Arj
+1  A: 

EDIT: Now with correct namespace.

This stylesheet:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:fixml="http://www.fixprotocol.org/FIXML-4-4"&gt;
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="fixml:A/@z">
        <xsl:call-template name="identity"/>
        <xsl:attribute name="new">
            <xsl:value-of
               select="concat('values present are x=',../@x,' and y=',../@y)"/>
        </xsl:attribute>
    </xsl:template>
</xsl:stylesheet>

With this input:

<R xmlns="http://www.fixprotocol.org/FIXML-4-4"&gt;
    <A x="foo" y="bar" z="">
        <B/>
        <C/>
    </A>
</R>

Output:

<R xmlns="http://www.fixprotocol.org/FIXML-4-4"&gt;
    <A x="foo" y="bar" z="" new="values present are x=foo and y=bar">
        <B></B>
        <C></C>
    </A>
</R>
Alejandro
+2  A: 

Updated: The OP has explained (only in a comment!):

There's something that's stopping these solutions working. The text XML I've put above actually has a root node that contains it all. The <R> node has an attribute like this : xmlns="http://www.fixprotocol.org/FIXML-4-4". Adding this attribute for some reason causes the template matching "A" to not work?!

This is probably as short as it can be:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:x="http://www.fixprotocol.org/FIXML-4-4"
 xmlns="http://www.fixprotocol.org/FIXML-4-4" exclude-result-prefixes="x">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="x:A[@z]">
  <A new="values present are x={@x} and y={@y}">
   <xsl:apply-templates select="node()|@*"/>
  </A>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<R xmlns="http://www.fixprotocol.org/FIXML-4-4"&gt;
    <A x="foo" y="bar" z="">
        <B/>
        <C/>
    </A>
</R>

the wanted, correct result is produced:

<R xmlns="http://www.fixprotocol.org/FIXML-4-4"&gt;
    <A new="values present are x=foo and y=bar" x="foo" y="bar" z="">
        <B></B>
        <C></C>
    </A>
</R>
Dimitre Novatchev
thanks Dimitre. please see the comment I've added above..
Arj
+1 for the AVT's
DevNull
thanks Dimitre. I somehow figured out the xmlns prob, and I saw you'd given the fullest answer. And I'll add additional stuff to the question next time! Thanks v much
Arj