Shorty
Shorty

Reputation: 55

XSLT Set attribute values from another XML file in different elements

It is a bit difficult to explain so I speak in XML..

XML-File which I want to transform

<Items>
<Item ID="1" Name="Student">
  <Cell Name=""/>
  <Cell Number=""/>
</Item>
<Item ID="1" Name="Professor">
  <Cell Name=""/>
  <Cell Number=""/>
</Item>

values.xml with all attribute values

<students>
  <count>2</count>
    <student number="1">
      <name>Peter</name>
      <number>1234</number>
    </student>
    <student number="2">
      <name>Stefan</name>
      <number>4567</number>
    </student>
</students>

desired output

    <Items>
        <Item ID="1" Name="Student">
            <Cell Name="Max"/>
            <Cell Number="1234"/>
        </Student>
        <Item ID="2" Name=Student>
            <Cell Name="Stefan"/>
            <Cell Number="4567"/>
        </Professor>
    </Items>

My Idea

    <xsl:variable name="total" select="document('values.xml')"/>

    <!-- Identity Transformation --> ||WORKS FINE||

    <!-- copy students n-times --> ||WORKS FINE||

<!-- set attribute: name --> ||DOESN'T WORK|| 
<xsl:template match="v:Items/Item/Cell[1]">
<xsl:param name="c" select="1"/>
<xsl:param name="values" select="$total/students/student[@number=$c]"/>    
    <xsl:copy>          
        <xsl:attribute name="Name">
                <xsl:copy-of select="$values/name"/>
            </xsl:attribute>                
            <xsl:apply-templates/>
    </xsl:copy>             
    <xsl:if test="$c &lt; $total/students/count">
        <xsl:apply-templates select=".">
            <xsl:with-param name="c" select="$c+1"/>                        
        </xsl:apply-templates>          
    </xsl:if>       
</xsl:template>

I get both attribute values in each element. But I want the first attribute value in the first (Cell-)element , the second attribute value in the second (Cell-)element and so on.

Upvotes: 3

Views: 349

Answers (1)

zx485
zx485

Reputation: 29042

EDIT: A shorter way to achieve this is to count the position in the XML and use that to get the index of the Cell and the student.

<xsl:template match="Items/Item">
    <xsl:variable name="idx"><xsl:number /></xsl:variable>  <!-- index of this 'Item' -->
    <xsl:variable name="values" select="$total/student[@number=$idx]"/> 
    <xsl:copy>          
        <xsl:copy-of select="@*" />                         <!-- copy attributes of this 'Item' -->
        <xsl:apply-templates select="Cell">                 
            <xsl:with-param name="cell" select="$values" /> <!-- pass current 'student' as parameter -->
        </xsl:apply-templates>
    </xsl:copy>             
</xsl:template>

<xsl:template match="Cell">
    <xsl:param name="cell" />
    <xsl:variable name="idx" select="position()" />
    <xsl:variable name="firstAttr" select="name(@*[1])" />  <!-- get the name of the first attribute of this 'Cell' -->
    <xsl:copy>          
        <xsl:attribute name="{$firstAttr}">                 <!-- recreate attribute -->
            <xsl:value-of select="$cell/*[$idx]"/>          <!-- use position() of this 'Cell' as index in the current 'student' -->
        </xsl:attribute>        
    </xsl:copy>             
</xsl:template>

The second template puts the nth child element of the current student into the first attribute of the nth Cell.

If you want to match only the student Cells, you can use an empty template to filter out the 'Professor' Items:

<xsl:template match="Items/Item[@Name='Professor']" />    

Then restrict the first template from above with a predicate:

<xsl:template match="Items/Item[@Name='Student']">
   <xsl:variable name="idx" select="@ID"></xsl:variable>
   ...

Upvotes: 1

Related Questions