2

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.

1
  • Good question (+1). See my answer for a completely "loopless" solution that is entirely in "push-style" and in the spirit of the XSLT language. :) Commented Jul 28, 2010 at 3:22

4 Answers 4

2

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>
Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

I went with this solution as it fitted my needs but all of the answers helped me out.
1

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>

2 Comments

+1 for elegant solution. But in order to learn XSLT, it would be good an explanation about why his transformation isn't working. Every else answer spot the context node inside for-each wich is a wrong explanation for the provided XSLT fragment.
@Alejandro: I agree, but I find it more important to let beginners know that in XSLT <xsl:template> and push-style are the really important things, not <xsl:for-each>
0

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.

1 Comment

Thanks a lot everybody - I've gone with a template solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.