Richard Knight
Richard Knight

Reputation: 267

XSLT multiple nodes same name same level

I have the following XML document:

<ReportParameters SP="prRptActivityDetail">
    <Parameter>
        <Name>Period Start Date</Name>
        <Type>Date</Type>
        <Control>DateTextbox</Control>
        <ControlName>dtePeriodStartDate</ControlName>
        <Validators>
            <Validator>Required</Validator>
            <Validator>DataTypeCheck</Validator>
            <Validator>StartBeforeEnd</Validator>
        </Validators>
    </Parameter>
</ReportParameters>

I have written an XSLT file to transform the above:

<xsl:for-each select="ReportParameters/Parameter/Validators">
   <xsl:choose>
       <xsl:when test="Validator='Required'">
           <span>
               <REQUIRED VALIDATOR CONTROL HERE>
           </span>
       </xsl:when>
       <xsl:when test="Validator='DataTypeCheck'">
           <span>
               <DATA TYPE CHECK VALIDATOR CONTROL HERE>
           </span>
       </xsl:when>
   </xsl:choose>

I've left out a lot of the XSLT for clarity.

For each parameter control (Period Start Date in this case) I wish to have all of the validators listed (3 in this case) placed on the page as validator controls, but I only get the first one when using for-each. I know why this is but I'm a complete newbie with xslt and don't know the syntax to get around this.

Any help much appreciated,

Rich.

Upvotes: 2

Views: 10102

Answers (4)

Flynn1179
Flynn1179

Reputation: 12075

I'd suggest replacing your <xsl:for-each tag with:

<xsl:apply-templates select="ReportParameters/Parameter/Validators/Validator" />

and include templates for each:

<xsl:template match="Validator[text()='Required']">
  ...
</xsl:template>

<xsl:template match="Validator[text()='DataTypeCheck']">
  ..
</xsl:template>

 etc.

As @kniemczak said, you're actually only looping through the parent element at the moment.

Upvotes: 1

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

When learning XSLT it is good to know that one should avoid using <xsl:for-each> unless this is really necessary.

Here is a simple and short "loopless" way to achieve the same in pure push style:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:variable name="vrtfValidatorNames">
  <validator type="Required" name="REQUIRED"/>
  <validator type="DataTypeCheck" name="DATA TYPE CHECK"/>
  <validator type="StartBeforeEnd" name="START BEFORE END"/>
 </xsl:variable>

 <xsl:variable name="vValidatorNames" select=
  "document('')/*/xsl:variable[@name='vrtfValidatorNames']/*"/>

 <xsl:template match="Validator">
   &lt;<xsl:value-of select="$vValidatorNames[@type=current()]/@name"/> VALIDATOR CONTROL HERE>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<ReportParameters SP="prRptActivityDetail">
    <Parameter>
        <Name>Period Start Date</Name>
        <Type>Date</Type>
        <Control>DateTextbox</Control>
        <ControlName>dtePeriodStartDate</ControlName>
        <Validators>
            <Validator>Required</Validator>
            <Validator>DataTypeCheck</Validator>
            <Validator>StartBeforeEnd</Validator>
        </Validators>
    </Parameter>
</ReportParameters>

the wanted result is produced:

   <REQUIRED VALIDATOR CONTROL HERE>

   <DATA TYPE CHECK VALIDATOR CONTROL HERE>

   <START BEFORE END VALIDATOR CONTROL HERE>

Upvotes: 1

Damien Ayers
Damien Ayers

Reputation: 1629

For this sort of task you don't need to explicitly loop over elements. It's not the XSLT way. Think more in terms of matching against the tree structure.

Something like this will work:

<xsl:if test="ReportParameters/Parameter/Validators/Validator='Required'">
    <span>
        <REQUIRED></REQUIRED>
     </span>
</xsl:if>
<xsl:if test="ReportParameters/Parameter/Validators/Validator='DataTypeCheck'">
    <span>
        <DATA></DATA>
    </span>
</xsl:if>

Also look into creating sub <xsl:template>s and calling them with <xsl:apply-templates> to handle smaller portions of the tree. For a large transform it should be a lot more maintainable.

Upvotes: 0

kniemczak
kniemczak

Reputation: 359

I think the issue is that you are looping over the 'Validators' collection. You are wanting to loop over all the instances of 'Validator'.

Try: (example I used in research)

<xsl:for-each select="ReportParameters/Parameter/Validators/Validator">
   <xsl:choose>
       <xsl:when test=".='Required'">
           <span>
               <REQUIRED VALIDATOR CONTROL HERE>
           </span>
       </xsl:when>
       <xsl:when test=".='DataTypeCheck'">
           <span>
               <DATA TYPE CHECK VALIDATOR CONTROL HERE>
           </span>
       </xsl:when>
   </xsl:choose>

Upvotes: 2

Related Questions