XSLT选择节点,其中元素值的子串不等于其他节点的元素值

时间:2015-09-19 11:57:35

标签: xml xslt

我是CSS新手,遇到了障碍。我有一个图像详细信息列表,如下所示。

<Files IsList="true">
  <Item>
    <FileSize>97 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_7vxsysv.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>108 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_9cxjbh7.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>109 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_9cxjbh7_Annotated.jpg</Name>
    <Description>(annotated)</Description>
  </Item>
  <Item>
    <FileSize>170 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_omau6bn.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>175 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_omau6bn_Annotated.jpg</Name>
    <Description>(annotated)</Description>
  </Item>
</Files>

所有图像都有一个原始文件,一些图像有一个带注释的副本(由描述和名称后缀标识)。

我需要显示所有图像(在一张包含3张图像的表中),逻辑:

  • 如果有带注释的副本,则仅显示带注释的版本
  • 如果没有带注释的副本显示原件。

Desired Outcome是一个选择语句,产生以下结果。

  <Item>
    <FileSize>97 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_7vxsysv.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>109 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_9cxjbh7_Annotated.jpg</Name>
    <Description>(annotated)</Description>
  </Item>
  <Item>
    <FileSize>175 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_omau6bn_Annotated.jpg</Name>
    <Description>(annotated)</Description>
  </Item>

我试图循环遍历所有节点并使用IF语句来确定显示的是哪个,但是因为我使用position()来确定从哪一个开始新行,这不起作用,因为并非所有图像都显示,抛掉行计算。

我认为select需要基于Name元素值的子字符串,并与其他Name元素的子字符串进行比较。

这可能吗?

2 个答案:

答案 0 :(得分:0)

我不知道你是否想要XSLT 1.0或XSLT 2.0,并且你不想要一个html表输出或xml输出(你似乎在这方面自相矛盾),所以这可能不完全符合您的要求,但可能会有所帮助。

这个XSLT 2.0样式表......

<xsl:transform
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">

<xsl:output method="html" version="5" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />


<xsl:template match="/">
  <hmtl>
    <head>
      <title>Table of items</title>
    </head>
    <body>
      <table>
        <tbody>
          <xsl:for-each-group
              select="Files/Item[
                (Description eq '(annotated)') or
                 not(
                   for $name in concat( substring-before( Name/text(), '.jpg'), '_Annotated.jpg') return
                     following-sibling::Item[ Name eq $name])]"
              group-by="(position() - 1) idiv 3">
            <tr>
              <xsl:apply-templates select="current-group()" />
            </tr>
          </xsl:for-each-group>
        </tbody>
      </table>
    </body>
  </hmtl>
</xsl:template>

<xsl:template match="Item">
  <td>
    <xsl:copy-of select="." />
  </td>
</xsl:template>

</xsl:transform>

...将转换此输入文档......

<Files IsList="true">
  <Item>
    <FileSize>97 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_7vxsysv.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>108 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_9cxjbh7.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>109 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_9cxjbh7_Annotated.jpg</Name>
    <Description>(annotated)</Description>
  </Item>
  <Item>
    <FileSize>170 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_omau6bn.jpg</Name>
    <Description>Picture</Description>
  </Item>
  <Item>
    <FileSize>175 kb</FileSize>
    <Type>image/jpeg</Type>
    <Name>CR_0_Pc_omau6bn_Annotated.jpg</Name>
    <Description>(annotated)</Description>
  </Item>
</Files>

...进入此输出html页面......

<!DOCTYPE HTML>
<hmtl>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <title>Table of items</title>
   </head>
   <body>
      <table>
         <tbody>
            <tr>
               <td>
                  <Item>
                     <FileSize>97 kb</FileSize>
                     <Type>image/jpeg</Type>
                     <Name>CR_0_Pc_7vxsysv.jpg</Name>
                     <Description>Picture</Description>
                  </Item>
               </td>
               <td>
                  <Item>
                     <FileSize>109 kb</FileSize>
                     <Type>image/jpeg</Type>
                     <Name>CR_0_Pc_9cxjbh7_Annotated.jpg</Name>
                     <Description>(annotated)</Description>
                  </Item>
               </td>
               <td>
                  <Item>
                     <FileSize>175 kb</FileSize>
                     <Type>image/jpeg</Type>
                     <Name>CR_0_Pc_omau6bn_Annotated.jpg</Name>
                     <Description>(annotated)</Description>
                  </Item>
               </td>
            </tr>
         </tbody>
      </table>
   </body>
</hmtl>

设计说明

分组(xsl:for-each-group)实际上只是将输出编组到3列表中。桌子将是粗糙的。如果您需要常规表来支持旧版浏览器,则需要在最后一行添加一些额外代码。

以下是您在关闭</tr>之前添加的内容,以使其成为常规

<xsl:for-each select="1 to 3 - count(current-group())">
  <td>&#160;</td>
</xsl:for-each>

xsl:for-each-group/@select中的XPath表达式为此解决方案带来了沉重的负担。它选择所有注释的Item元素或没有匹配的带注释的兄弟元素。这基本上是您要处理输出的内容。

更新

这是一个等效的XSLT 1.0解决方案..

<xsl:transform
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    extension-element-prefixes="exsl"
    version="1.0">

<xsl:output method="html" doctype-system="" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />    

<xsl:template match="/">
  <hmtl>
    <head>
      <title>Table of items</title>
    </head>
    <body>
      <table>
        <tbody>
          <xsl:variable name="Items">
            <xsl:apply-templates select="Files/Item" mode="filter-items" />
          </xsl:variable>
          <xsl:apply-templates select="exsl:node-set($Items)/Item" mode="row" />
        </tbody>
      </table>
    </body>
  </hmtl>
</xsl:template>

<xsl:template match="Item[Description='(annotated)']" mode="filter-items">
  <xsl:copy-of select="." />
</xsl:template>

<xsl:template match="Item[Description!='(annotated)']" mode="filter-items">
  <xsl:variable name="name" select="concat( substring-before( Name/text(), '.jpg'), '_Annotated.jpg')" />
  <xsl:if test="not( following-sibling::Item[ Name = $name])">
    <xsl:copy-of select="." />
  </xsl:if>
</xsl:template>

<xsl:template match="Item[1]" mode="row">
  <tr>
    <xsl:apply-templates select=". | following-sibling::Item[position() &lt;= 2]" mode="cell" />
    <xsl:variable name="empty-cols" select=" 3 - count(. | following-sibling::Item[position() &lt;= 2])" />
    <xsl:for-each select="(//node())[position() &lt;= $empty-cols]">
      <td>&#160;</td>
    </xsl:for-each>
  </tr>    
</xsl:template>

<xsl:template match="Item" mode="row" />

<xsl:template match="Item" mode="cell">
  <td>
    <xsl:copy-of select="." />
  </td>    
</xsl:template>

</xsl:transform>

以上假设您的XSLT 1.0引擎支持exslt。请注意,没有理由不升级到XSLT 2.0。如果您的平台是浏览器,那么您可以使用Saxon CE来实现升级。输出表与the Piez method保持一致。如果效率太低,还有其他选择。

答案 1 :(得分:0)

  
      
  • 如果有带注释的副本,则仅显示带注释的版本
  •   
  • 如果没有带注释的副本,则显示原件。
  •   
     

Desired Outcome是一个选择语句,产生以下结果。

您可以使用以下表达式选择所需的项目:

select="Item[Description='(annotated)' or not(key('anno', substring(Name, 1, 15)))]"/>

定义一个键之后:

<xsl:key name="anno" match="Item[Description='(annotated)']" use="substring(Name, 1, 15)" />

这假定所有项目名称长度恰​​好为15个字符。如果这个假设不正确,那就会变得更复杂。

请注意,仅选择项目是不够的,因为所选项目保持其原始上下文。为了使所选项目成为兄弟姐妹,您需要它们复制到变量中。以下是完整样式表的示例,将所选项目分为三列:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="html" encoding="utf-8"/>

<xsl:key name="anno" match="Item[Description='(annotated)']" use="substring(Name, 1, 15)" />

<xsl:template match="/Files">
    <xsl:variable name="items">
        <xsl:copy-of select="Item[Description='(annotated)' or not(key('anno', substring(Name, 1, 15)))]"/>
    </xsl:variable>
    <xsl:variable name="cols" select="3" />
    <table border="1">
        <xsl:for-each select="exsl:node-set($items)/Item[position() mod $cols = 1]" >
        <tr>
            <xsl:apply-templates select=". | following-sibling::Item[position() &lt; $cols]"/>
        </tr>
        </xsl:for-each>
    </table>
</xsl:template>

<xsl:template match="Item">
    <td><img src="{Name}"/></td>
</xsl:template>

</xsl:stylesheet>