bbfire
bbfire

Reputation: 3598

XSLT to copy XML doc and add attribute plus generated value under certain conditions

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?!

Upvotes: 1

Views: 1924

Answers (3)

user357812
user357812

Reputation:

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">
    <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">
    <A x="foo" y="bar" z="">
        <B/>
        <C/>
    </A>
</R>

Output:

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

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243579

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">
    <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">
    <A new="values present are x=foo and y=bar" x="foo" y="bar" z="">
        <B></B>
        <C></C>
    </A>
</R>

Upvotes: 3

stapeluberlauf
stapeluberlauf

Reputation: 567

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">
  <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>

Upvotes: 1

Related Questions