tags:

views:

79

answers:

4

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"&gt;
 <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> 
A: 

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.

Oded
Actually this xml is generated by js-treeviewcontrol i'm using.so i can't change the xml.
A control? So this is _consumed_ by the control and generated elsewhere?
Oded
The inputxml i have provided is generated by control,now i've to structure it according to the desired output.
And where does the control get its input? Perhpas you can use that instead?
Oded
The input to control is same as the OUPUTEXPECTED in this case. But as using the control user can change/add node, i need to convert control's output back in well structured XML.
My xslt is not good enough to help with this. Sorry.
Oded
+1  A: 

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"&gt;
   <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.

devio
Actually i'm looking for generic solution which can be applied to any such xml, where node structure is not known beforehand.
You did not mention your requirement of a "generic solution" in your question.
devio
xslt should be independent of node names, e.g. here catalog /cd/title is used to get desired output, rather if we apply my xslt where matching criteria's are independent of node names, the out put generated is not consistent if same node name reappear in xml, so it's problem just with unique id ,thats why its failing , where i need solution.
+2  A: 

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.

Tomalak
Thanks it works!Can you please also help me if the attribute img="ATTR" value should be added as attribute to the element, i'm modifying my original port.
please help me with modified xml...
A: 

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"&gt;
    <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.

Robert Christie
I don't want node name to be hard coded inside xslt.Tomalaks's solution works great, just i need now extra is attribute should be taken care of.
@cb160: Note that the question has changed considerably.
Tomalak