XSL:有没有使用变量在谓词中的快速替代方法?

时间:2010-12-03 10:38:37

标签: xslt xslt-1.0

以微弱的方式编辑澄清。系统通过XSL处理器将XML转换为另一种XML。

我正在为具有插件卡的机器做一些XSL规则。这些卡将向主要主机提供他们自己的XSL规则,然后主要将这些规则合并为一个超级XSL文件以便自己处理,并且还将其发送到已经HTTP到机器上的Web浏览器。浏览器用于设置机器的配置项,XSL用于更改和/或隐藏和/或取消隐藏某些XML。为了简化我的查询,我发明了一个小例子。

当用户将项目配置为B类型(可选择A,B,C等几种类型)时,接下来的两个项目也将被更改(以某种方式)。在真实交易中,有一个属性“hidden”设置为true或false,以及设置的子元素。真实事物中的下两个项目将被隐藏,并且子元素也会被更改。

当用户将项目从类型B更改为类型A时,我需要确定哪些其他节点需要将其“隐藏”属性设置为false。用户将根据需要更改子元素。

所有这些都是“循环”,并且是有序的,因此如果一个节点设置为B类,并且它是最后一个节点(没有后续兄弟节点),则受影响的节点是该集合中的第一个节点。 (在XPath术语中,如果node [4]是B类,则节点[1]和[2]将被隐藏和更改)。

所以,对于我的例子,我有一个输入XML:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="false"><source name="off"/></midline>
  <midline type="A" name="mid4" hidden="false"><source name="off"/></midline>
</topline>

并且XSL会将其更改为:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>

现在,如果用户改变主意,并将mid2更改为类型A:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>

然后XSL将取消隐藏在mid2的兄弟之后的循环,因此结果应为:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="false"><source name="input 1"/></midline>
</topline>

这是我正在努力的第二步。我所做的解决方法是,对我来说,相当难看,但也许是不可避免的,因为我想要达到的目标并不是XSL友好。

我做了什么:

<xsl:for-each select="topline">
  <xsl:for-each select="midline">
    <xsl:variable name="masterPosition" select="position()"/>
    <xsl:choose>
      <xsl:when test="@type='B'>
        hide the next two nodes. This is easy: 
            translate($masterPosition, '1234', '2341')
        works nicely.
      </xsl:when>
      <xsl:otherwise>
          <xsl:variable name="prior1" select="translate(masterPosition, '1234', '4123')"/>
          <xsl:variable name="test1" select="../midline[$prior1]/source[1]/@name='off'"/>
          this second line doesn't work: I only get the first node, always. 
          So instead I have 
          <xsl:variable name="test2">
              <xsl:choice>
                  <xsl:when test="$masterPosition='1'"><xsl:value-of select="../midline[4]/source"/></xsl:when>
                  and so on for the other masterPositions
              </xsl:choice>
          </xsl:variable>
          and this is repeated for a few other variables, one each for each relevant
          prior position and for the fields I need to change. I then use these
          variables to change the XML - there's something in the main machine's
          processing to enable this, I believe it is non-standard, so please ignore: 
          (At least it doesn't run against Xalan).
          <set key={$test2}/@hidden, value="false")/>             
      </xsl:otherwise>
    </xsl:choose>
  <xsl:for-each>
</xsl:for-each>

有没有更优雅的方式来做到这一点你能想到的?如果没有,请不要担心,我相信我的黑客会工作,也不应该消耗太多的MIPS。

我无法使用<xsl:key>,因为我们的系统没有应对:我们有多个XSL源被合并为一个,而且一个贪婪的脚本(我的控制之外)根本无法理解{{1如果有使用密钥的解决方案,我就无法使用它。

由于 理查德

2 个答案:

答案 0 :(得分:1)

根据模板匹配,这是一种无需键的方法:

<!-- start with the identity transform -->
<xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*" />
  </xsl:copy>
</xsl:template>

<!-- <midline>s that have two preceding siblings -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) &gt; 1]">
  <xsl:apply-templates select="." mode="source">
    <xsl:with-param name="circular" select="
      preceding-sibling::midline[position() &lt;= 2]
    " />
  </xsl:apply-templates>
</xsl:template>

<!-- <midline>s that have one preceding sibling -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 1]">
  <xsl:apply-templates select="." mode="source">
    <xsl:with-param name="circular" select="
      (preceding-sibling::midline|../midline[last()])
    " />
  </xsl:apply-templates>
</xsl:template>

<!-- <midline>s that have no preceding sibling -->
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 0]">
  <xsl:apply-templates select="." mode="source">
    <xsl:with-param name="circular" select="
      ../midline[position() &gt; last() - 2]
    " />
  </xsl:apply-templates>
</xsl:template>

<!-- this template creates a new node with the wanted source -->
<xsl:template match="midline" mode="source">
  <xsl:param name="circular" select="." />
  <xsl:variable name="prevB" select="$circular[@type = 'B' and not(@mode = off)]" />
  <xsl:copy>
    <xsl:copy-of select="@*" />
    <xsl:attribute name="hidden">
      <xsl:value-of select="boolean($prevB)" />
    </xsl:attribute>
    <xsl:choose>
      <xsl:when test="$prevB">
        <xsl:copy-of select="$prevB/source" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="source" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:copy>
</xsl:template>

对于您的样本输入,输出将是:

<topline>
  <midline type="A" name="mid1" hidden="false"><source name="off"/></midline>
  <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline>
  <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline>
  <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline>
</topline>

请注意,您的描述与您的示例输出相矛盾 - 这是严格遵循您的描述时所获得的,除非我误解了您。

<xsl:with-param>用于传递两个节点,即“循环前兄弟节点”(或@type=B'个节点的空节点集)。

模板<xsl:template match="midline" mode="source">使用该参数:如果它在那里,它会检查您的情况(即其中一个是@type='B'而不是@mode='off')。

如果此类节点存在,则会复制其<source>,否则会复制原始<source>

答案 1 :(得分:1)

要进行逆向转换,您需要

   

                     

  

  

                                       

将此转换应用于以下(提供的)XML,其中用户刚刚将type='B'更改为type='A'

<topline>
    <midline type="A" name="mid1" hidden="false">
        <source name="off"/>
    </midline>
    <midline type="A" name="mid2" hidden="false">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid3" hidden="true">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid4" hidden="true">
        <source name="input 1"/>
    </midline>
</topline>

生成了想要的,正确的结果(为前B类型的下两个圆形兄弟姐妹设置了hidden="false"):

<topline>
   <midline type="A" name="mid1" hidden="false">
      <source name="off"/>
   </midline>
   <midline type="A" name="mid2" hidden="false">
      <source name="input 1"/>
   </midline>
   <midline type="A" name="mid3" hidden="false">
      <source name="input 1"/>
   </midline>
   <midline type="A" name="mid4" hidden="false">
      <source name="input 1"/>
   </midline>
</topline>

使用此源XML文件(真正的循环兄弟):

<topline>
    <midline type="A" name="mid1" hidden="true">
        <source name="off"/>
    </midline>
    <midline type="A" name="mid2" hidden="false">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid3" hidden="false">
        <source name="input 1"/>
    </midline>
    <midline type="A" name="mid4" hidden="true">
        <source name="input 1"/>
    </midline>
</topline>

再次生成想要的正确结果

请注意:使用XPath mod运算符来确定圆形兄弟位置。