XSLT sum concatenated instead of summed - xslt

I have the following input
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<carac NAME="aaa" NOT="10"/>
<value VAL="1"/>
</cd>
<cd>
<carac NAME="aaa" NOT="10"/>
<value VAL="2"/>
</cd>
<cd>
<carac NAME="aaa" NOT="20"/>
<value VAL="3"/>
</cd>
<cd>
<carac NAME="aaa" NOT="10"/>
<value VAL="4"/>
</cd>
<cd>
<carac NAME="bbb" NOT="30"/>
<value VAL="5"/>
</cd>
<cd>
<carac NAME="bbb" NOT="30"/>
<value VAL="6"/>
</cd>
<cd>
<carac NAME="ccc" NOT="40"/>
<value VAL="7"/>
</cd>
<cd>
<carac NAME="ccc" NOT="50"/>
<value VAL="8"/>
</cd>
</catalog>
and I want to get for every different NAME the sum of all the different NOT, so if for the same NAME the NOT is repeated, it has to be summed only once.
The output for this example has to be: aaa30 bbb30 ccc90
My XSL looks like this, but instead of giving the result I want is showing aaa1020 bbb30 ccc4050
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="nameList" match="catalog/cd/carac" use="#NAME"/>
<xsl:key name="notList" match="catalog/cd/carac" use="concat(#NAME,'_',#NOT)"/>
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="//carac[generate-id()=generate-id(key('nameList', #NAME)[1])]">
<xsl:variable name="name" select="./#NAME"/>
<xsl:variable name="lines">
<xsl:for-each select="//carac[generate-id()=generate-id(key('notList',concat($name,'_',#NOT))[1])]">
<noti>
<xsl:value-of select="#NOT"/>
</noti>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$name"/>
<xsl:value-of select="sum($lines)"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Try it this way?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="nameList" match="catalog/cd/carac" use="#NAME"/>
<xsl:key name="notList" match="catalog/cd/carac" use="concat(#NAME, '_', #NOT)"/>
<xsl:template match="/catalog">
<html>
<body>
<xsl:for-each select="cd/carac[generate-id()=generate-id(key('nameList', #NAME)[1])]">
<xsl:value-of select="#NAME"/>
<xsl:variable name="current-group" select="key('nameList', #NAME)" />
<xsl:value-of select="sum($current-group[generate-id()=generate-id(key('notList', concat(#NAME, '_', #NOT))[1])]/#NOT)"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Related

How to select the preceding-sibling inside a for each loop?

My XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>
<name>Empire Burlesque1 </name>
</title>
</cd>
<cd>
<title>
<name>Empire Burlesque 2</name>
</title>
</cd>
<cd>
<title>
<name>Empire Burlesque 2</name>
</title>
</cd>
</catalog>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="catalog/cd">
<xsl:call-template name="currentValue" />
<xsl:call-template name="prevValue" />
</xsl:for-each>
</xsl:template>
<xsl:template name="currentValue">
<xsl:value-of select="title/name" />
</xsl:template>
<xsl:template name="prevValue">
<xsl:value-of select="preceding-sibling::title[name][1]" />
</xsl:template>
</xsl:stylesheet>
The preceding sibling does not print anything. I want to store both in different variables and compare them. Can you help me pointing what is wrong here?
Currently, when you are checking for the preceding element you are positioned on cd element, and that only has other cd elements as siblings. Therefore, the expression you want is this:
<xsl:value-of select="preceding-sibling::cd[1]/title/name" />

How to wrap input xsml in c data using xslt

input to the xslt will be like below:
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
Output of the xsl should be like below:
<Output>
<![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
]]>
</Output>
I have written below code but < and > are not replacing with < > in the output.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="output">
<output>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:text disable-output-escaping="yes"> <?xml version="1.0" encoding="UTF-8"?> </xsl:text>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</output>
</xsl:variable>
<xsl:copy-of select="$output"/>
</xsl:template>
</xsl:stylesheet>
Need to create CDATA out of variable like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="output">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</xsl:variable>
<output>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:text disable-output-escaping="yes">
</xsl:text>
<xsl:text disable-output-escaping="yes"><?xml version="1.0" encoding="UTF-8"?></xsl:text>
<xsl:copy-of select="$output"/>
<xsl:text disable-output-escaping="yes">
]]></xsl:text>
</output>
</xsl:template>
</xsl:stylesheet>
CDATA sections are really just an alternate escaping mechanism: at an XML level your desired result is the <Output/> element with text content that happens to be the serialized output. DataPower includes a <dp:serialize/> extension that can do this.
I think a working stylesheet should look something like
<?xml version="1.0"?>
<xsl:stylesheet
version="1.0"
extension-element-prefixes="dp"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions">
<xsl:output method="xml" cdata-section-elements="Output"/>
<xsl:template match="/">
<Output>
<dp:serialize select="."/>
</Output>
</xsl:template>
</xsl:stylesheet>
(This has always been a little bit odd construction, because you can embed XML in XML directly; if you control the application and schema you might consider trying to change it so you don't need to XML parse the text content of an XML element.)

XSLT performance issue with large data

having performance issues with my xslt code:
this is my input file:
<?xml version="1.0" encoding="UTF-8"?>
<Products>
<Product ID="111111" Type="Item" ParentID="7402">
<Name>ABC</Name>
<Values>
<Value AttributeID="11">8.00</Value>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="13">0.18</Value>
</Values>
<Product ID="B582B65D" Type="UID" ParentID="111111">
<Values>
<Value AttributeID="11">8.00</Value>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="13">0.18</Value>
<Value AttributeID="14">0.18</Value>
</Values>
</Product>
</Product>
<Product ID="222222" Type="Item" ParentID="7402">
<Name>XYZ</Name>
<Values>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="13">8.00</Value>
<Value AttributeID="15">0.18</Value>
</Values>
<Product ID="B582B65D" Type="UID" ParentID="111111">
<Values>
<Value AttributeID="11">8.00</Value>
<Value AttributeID="12">8.00</Value>
<Value AttributeID="16">0.18</Value>
<Value AttributeID="18">0.18</Value>
</Values>
</Product>
</Product>
</Products>
and this is my transformation code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:output method="xml" indent="yes" />
<xsl:param name="file2" select="document('Mapping.xml')" />
<xsl:template match="/Products">
<Products>
<xsl:for-each select="Product">
<xsl:call-template name="item" />
</xsl:for-each>
</Products>
</xsl:template>
<xsl:template name="item">
<Product type="{./#Type}" ID="{./#ID}">
<xsl:for-each select="./Values/Value">
<xsl:variable name="Idval" select="#AttributeID" />
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]/#group}">
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]}">
<xsl:attribute name="ID"><xsl:value-of select="$Idval"/></xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:element>
</xsl:for-each>
<xsl:call-template name="uid" />
</Product>
</xsl:template>
<xsl:template name="uid">
<Product type="{./Product/#Type}" ParentId="{./Product/#ParentID}">
<xsl:for-each select="./Product/Values/Value">
<xsl:variable name="Idval" select="#AttributeID" />
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]/#group}">
<xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]}">
<xsl:attribute name="ID"><xsl:value-of select="$Idval"/></xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:element>
</xsl:for-each>
</Product>
</xsl:template>
</xsl:stylesheet>
above xslt is using below xml file for mapping attribute id to corresponding name and group
Mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<Groups>
<AttributeID ID="11" group="Pack1">Height</AttributeID>
<AttributeID ID="12" group="Pack2">Width</AttributeID>
<AttributeID ID="13" group="Pack1">Depth</AttributeID>
<AttributeID ID="14" group="Pack3">Length</AttributeID>
<AttributeID ID="15" group="Pack3">Lbs</AttributeID>
<AttributeID ID="16" group="Pack4">Litre</AttributeID>
</Groups>
Replace the use of expressions like
select="$file2//Groups/AttributeID[#ID=$Idval]"
with a key:
<xsl:key name="ID" match="Groups/AttributeID" use="#ID"/>
and then
select="key('ID', $IDval, $file)"/>
Alternatively, Saxon-EE will do this optimization for you automatically.
The key() function with 3 arguments is XSLT 2.0 syntax. If you have the misfortune to be using XSLT 1.0, you have to write a dummy xsl:for-each that makes $file the context item, because key() will only select within the document containing the context item.
Define a key for the cross document lookup: <xsl:key name="by-id" match="Groups/AttributeID" use="#ID"/>, then (assuming an XSLT 2.0 processor) you can simplify expressions like <xsl:element name="{$file2//Groups/AttributeID[#ID=$Idval]/#group}"> to <xsl:element name="{key('by-id', #AttributeID, $file2)/#group">. Make the same change for the other cross references you have, i.e. all those $file2//Groups/AttributeID[#ID=$Idval] expressions should use the key lookup.
Making the simplified assumption that your second file isn't too big, you want to fold the values there into your template. It would work with XSLT 1.0 too. Something like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<Products>
<xsl:apply-templates select="/Products/Product" />
</Products>
</xsl:template>
<xsl:template match="Product">
<xsl:element name="Product">
<xsl:apply-templates select="#*" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Name">
<Name>
<xsl:value-of select="." />
</Name>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="Values">
<Values>
<xsl:apply-templates />
</Values>
</xsl:template>
<!-- Templates for individual AttributeIDs, only when there are few -->
<xsl:template match="Value[#AttributeID='11']">
<Pack1>
<xsl:element name="Height">
<xsl:attribute name="ID">
<xsl:value-of select="#AttributeID" />
</xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</Pack1>
</xsl:template>
<!-- Repeat for the other AttributeID values -->
</xsl:stylesheet>
(Typed off my head, will contain typos)
Of course if it is big Michael's advice is the best course of action.

Demerge the merged cell in XSLT

Can someone help me with the below xslt question.
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="T">1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 2</title>
<artist rowmerge="F">Sample 2</artist>
<price rowmerge="T"></price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F">3</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="T">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="T">4</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="T"></title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="T"></price>
<year>1988</year>
</cd>
</catalog>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="f">1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 2</title>
<artist rowmerge="F">Sample 2</artist>
<price rowmerge="f">1</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F">3</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="F">4</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="F">4</price>
<year>1988</year>
</cd>
</catalog>
If rowmerge attribute is 'T' in the first cd for any tag (title/artist/price)then I need to copy the price value from first cd to next cd. I am new to xslt.
You first should read up on the XSLT Identity Template, which on its own will copy nodes from the source document to the output.
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
What this means is that you only need to write templates for the nodes you wish to transform. Considering just the price elements for now, you are trying to amend price elements which have a rowmerge set to "T" and which are empty. In this case, you want to copy the price from the first most preceding 'cd'. This is achieved like so:
<xsl:template match="price[#rowmerge='T'][not(normalize-space())]">
<price>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/price" />
</price>
</xsl:template>
So, it is very similar to the identity template, but it has the extra xsl:value-of statement to copy the value from the preceding node. Or rather the price value from the preceding node of the parent cd element.
Of course, you could repeat this template for each of the possible child elements of cd, but this would be a lot of repetitive coding. Better would be to have a more generic template to cover all cases:
<xsl:template match="*[#rowmerge='T'][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/*[name() = name(current())]" />
</xsl:copy>
</xsl:template>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[#rowmerge='T'][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/*[name() = name(current())]" />
</xsl:copy>
</xsl:template>
<xsl:template match="#rowmerge[. = 'T']">
<xsl:attribute name="rowmerge">F</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Do note there is also a template to convert the rowmerge attributes from having a value of T to F.
EDIT: In answer to your comment, if you have to look more than one sibling back (that is to say, you have two consecutive elements that are empty), then try one of these two expressions
<xsl:value-of select="../preceding-sibling::*[*[name() = name(current())][normalize-space()]][1]/*[name() = name(current())]" />
<xsl:value-of select="(../preceding-sibling::*/*[name() = name(current())][normalize-space()])[last()]" />
EDIT 2: If the nodes contain more than just text, then to copy all the child elements, you use xsl:copy-of instead of xsl:value-of. For example...
<xsl:copy-of select="../preceding-sibling::*[1]/*[name() = name(current())]/node()" />
Note the use of node() on the end, to ensure only the child nodes are copied, not the price (for example) element itself.

XSLT: Converting a structure of items based on attribute values

Can somebody help me with the following problem, here is the input XML, the XSLT I'm using and the expected output. Actually I know it is because of unique generateid not getting generated this xslt failing to generate desired output, but I don't know where that code should be inserted.
XML:
<item id="N65537" text="catalog">
<item id="N65540" text="cd">
<item id="N65542" text="title">
<item id="N65543" img="VAL" text="Empire Burlesque" />
</item>
<item id="N65545" text="artist">
<item id="N65546" img="VAL" text="Bob Dylan" />
</item>
<item id="N65548" text="country">
<item id="N65549" text="attr1" img="ATTR">
<item id="N65549_N65549" text="primary" img="ATTRVAL" />
</item>
<item id="N65550" img="VAL" text="USA" />
</item>
<item id="N65552" text="company">
<item id="N65553" text="attr2" img="ATTR">
<item id="N65553_N65553" text="main" img="ATTRVAL" />
</item>
<item id="N65554" img="VAL" text="Columbia" />
</item>
<item id="N65556" text="price">
<item id="N65557" img="VAL" text="10.90" />
</item>
<item id="N65559" text="year">
<item id="N65560" img="VAL" text="1985" />
</item>
</item>
<item id="N65563" text="cd">
<item id="N65565" text="title">
<item id="N65566" img="VAL" text="Hide your heart" />
</item>
<item id="N65568" text="artist">
<item id="N65569" img="VAL" text="Bonnie Tyler" />
</item>
<item id="N65571" text="country">
<item id="N65572" img="VAL" text="UK" />
</item>
<item id="N65574" text="company">
<item id="N65575" img="VAL" text="CBS Records" />
</item>
<item id="N65577" text="price">
<item id="N65578" img="VAL" text="9.90" />
</item>
<item id="N65580" text="year">
<item id="N65581" img="VAL" text="1988" />
</item>
</item>
</item>
XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:call-template name="dispatch">
<xsl:with-param name="nodes" select="node()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="dispatch">
<xsl:param name="nodes"/>
<xsl:choose>
<xsl:when test="text()">
<xsl:call-template name="apply" >
<xsl:with-param name="select" select="node()" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="apply" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="apply">
<xsl:param name="select" select="node()" />
<xsl:for-each select="$select">
<xsl:if test='local-name() !=""'>
<xsl:variable name="ename">
<xsl:for-each select="#*">
<xsl:if test='name()="img1"'>
<xsl:text><xsl:value-of select="." /></xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="aname">
<xsl:for-each select="#*">
<xsl:if test='name()="img"'>
<xsl:text><xsl:value-of select="." /></xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="#*">
<xsl:variable name="tname">
<xsl:text><xsl:value-of select="." /></xsl:text>
</xsl:variable>
<xsl:choose>
<xsl:when test='name() ="text" and normalize-space($ename) = "VAL" and normalize-space($aname) != "ATTR"'>
<xsl:element name="{$tname}">
<xsl:for-each select="$select">
<xsl:call-template name="dispatch"/>
</xsl:for-each>
</xsl:element>
</xsl:when>
<xsl:when test='name() ="text" and normalize-space($ename) = "VAL" '>
<xsl:value-of select="$tname" />
</xsl:when>
<xsl:when test='name() ="text" and normalize-space($aname) = "ATTR"'>
<xsl:attribute name="id"><xsl:value-of select="$aname" /></xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Expected output:
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country attr1="primary">USA</country>
<company attr2="main">Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
EDIT: Modified answer after detail was added to the question.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- normal items become an ordinary element -->
<xsl:template match="item">
<xsl:element name="{#text}">
<!-- attributes must be created before any other contents -->
<xsl:apply-templates select="item[#img='ATTR']" />
<!-- now process sub-elements and values (i.e. "anything else") -->
<xsl:apply-templates select="item[not(#img='ATTR')]" />
</xsl:element>
</xsl:template>
<!-- items with "ATTR" become an attribute -->
<xsl:template match="item[#img='ATTR']">
<xsl:attribute name="{#text}">
<xsl:value-of select="item[#img='ATTRVAL']/#text" />
</xsl:attribute>
</xsl:template>
<!-- items with "VAL" become a simple text -->
<xsl:template match="item[#img='VAL']">
<xsl:value-of select="#text" />
</xsl:template>
</xsl:stylesheet>
gives
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country attr1="primary">USA</country>
<company attr2="main">Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
The stylesheet works because the XSL processor chooses templates based on the specificity of their match expressions. match="item[#img='ATTR']" is more specific than match="item", so for each <item> processed (through <xsl:apply-templates select="item" />) the engine picks the right template automatically.
The main problem I see in your XSLT solution is that you use xsl:if and xsl:choose instead of 'select' to filter nodes. This makes your XSLT difficult to read and understand (at least for me).
Try this:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes"/>
<xsl:template match="/item[#text='catalog']">
<catalog>
<xsl:apply-templates select="item[#text='cd']"></xsl:apply-templates>
</catalog>
</xsl:template>
<xsl:template match="item[#text='cd']">
<cd>
<title><xsl:value-of select="item[#text='title']/item[#img1='VAL']/#text"/></title>
<artist><xsl:value-of select="item[#text='artist']/item[#img1='VAL']/#text"/></artist>
<country><xsl:value-of select="item[#text='country']/item[#img1='VAL']/#text"/></country>
<company><xsl:value-of select="item[#text='company']/item[#img1='VAL']/#text"/></company>
<price><xsl:value-of select="item[#text='price']/item[#img1='VAL']/#text"/></price>
<year><xsl:value-of select="item[#text='year']/item[#img1='VAL']/#text"/></year>
</cd>
</xsl:template>
</xsl:stylesheet>
Solution does not cover the ATTR nodes, since they are not part of described result.
If you can possibly change the input xml, do so. XML is supposed to carry some meaning in the tag names and in its structure. Calling everything item just makes it unreadable.
Making such a change will also allow you to write readable XSLT that doesn't resort to node hierarchy selector tricks.
How about this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<catalog>
<xsl:for-each select="/item[#text='catalog']/item[#text='cd']">
<cd>
<xsl:for-each select="item">
<xsl:variable name="ename" select="string(#text)"/>
<xsl:variable name="value" select="item/#text"/>
<xsl:element name="{$ename}">
<xsl:value-of select="$value"/>
</xsl:element>
</xsl:for-each>
</cd>
</xsl:for-each>
</catalog>
</xsl:template>
</xsl:stylesheet>
Not as nice as Tomalaks solution - but maybe slightly clearer as to the intention.