简体   繁体   English

XSLT多个节点同名同级别

[英]XSLT multiple nodes same name same level

I have the following XML document: 我有以下XML文档:

<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: 我已经写了一个XSLT文件来转换上面的内容:

<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. 为了清楚起见,我省略了很多XSLT。

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. 对于每个参数控件(在这种情况下为“ Period Start Date”),我希望将所有列出的验证器(在这种情况下为3)作为验证器控件放置在页面上,但是在使用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. 我知道为什么会这样,但是我是xslt的完全新手,也不知道解决该问题的语法。

Any help much appreciated, 任何帮助,不胜感激,

Rich. 丰富。

I think the issue is that you are looping over the 'Validators' collection. 我认为问题在于您正在遍历“ Validators”集合。 You are wanting to loop over all the instances of 'Validator'. 您想遍历“ 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. 在学习XSLT时,最好知道应该避免使用<xsl:for-each>除非确实有必要。

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 : 当此转换应用于提供的XML文档时

<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:for-each标记替换为:

<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. 正如@kniemczak所说,您目前实际上只循环遍历父元素。

For this sort of task you don't need to explicitly loop over elements. 对于此类任务,您无需显式循环元素。 It's not the XSLT way. 这不是XSLT方式。 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. 另外,研究创建子<xsl:template>并使用<xsl:apply-templates>调用它们以处理树的较小部分。 For a large transform it should be a lot more maintainable. 对于大型转换,它应该更具可维护性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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