简体   繁体   中英

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.

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>

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>

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM