应用模板会导致多个项目作为某个'cast as'表达式中的值,value-of不会

时间:2014-09-15 13:51:06

标签: xslt

我想将包含在(嵌套)列表中的列表标题的TEI / xml文档转换为html。因此,当我遇到其中一个标题时,我需要在转换实际标题之前启动列表处理。

My(xslt-2.0)样式表忽略head[(parent::list)]元素,而是在匹配“list”的模板中处理这些情况:这会创建一些环境('section'),如果列表中有一个“head”子元素,它将head [(parent :: list)]的匹配模板以非默认模式应用于head子项,然后通过将ul / ol与项目放在一起。但是这会导致以下错误消息:

A sequence of more than one item is not allowed as the value in 'cast as' expression

我不能简单地使用<xsl:value-of select="child::head">(这不会导致错误),因为标题可以包含我需要呈现的其他元素,例如分页元素。

以下是示例文档的摘录:

<div>
  <list>
    <head>
      <pb facs="facs:W0013-0005" n="[2]"/>
      <lb n="1"/>PRIMUS TOMUS,
    </head>
    <item>
      <lb n="2"/>De
      <list>
        <item><lb n="3"/>Potestate ecclesiae, prior et posterior.</item>
        <item><lb n="4"/>Potestate civili.</item>
      </list>
    </item>
  </list>
</div>

这是我的样式表

的摘要
<!-- In a first approach, ignore headings when they are inside a list -->
<xsl:template match="head[(parent::list)]" />
<!-- Here is what should be done with them, but this needs to be called somewhere -->
<xsl:template match="head[(parent::list)]" mode="listIsDone">
  <h4>
<!-- this works, but is not what i need: <xsl:value-of select="."/> -->
    <xsl:apply-templates />
  </h4>
</xsl:template>
<!-- Here is how headings (outside of lists) are handled -->
<xsl:template match="head[not(parent::list)]">
  <h3>
    <xsl:apply-templates/>
  </h3>
</xsl:template>
<!-- Here is how lists are handled -->
<xsl:template match="list">
  <xsl:choose>
<!-- When the list contains a heading, then transform it according
     to the headings-inside-of-lists template -->
    <xsl:when test="child::head">
      <section>
        <xsl:for-each select="child::head"> 
          <xsl:apply-templates select="." mode="listIsDone"/>
        </xsl:for-each>
<!-- Then add an unordered list for the list "contents"/items -->
        <ul>
          <xsl:apply-templates/>
        </ul>
      </section>
    </xsl:when>
<!-- If the list does not contain a heading, add only the unordered list -->
    <xsl:otherwise>
      <ul>
        <xsl:apply-templates/>
      </ul>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

这是我想要的 html

<section>
  <h4><!-- Here begins output of the p-matching template --> | <div class="pageNumbers">
                <a class="pageNo" href="someURL-gotten-from-the-facs-attribute">11</a>
         </div><!-- Here ends output of the p-matching template -->
      PRIMUS TOMUS,
  </h4>
  <ul>
    <li> De
      <ul>
        <li>Potestate ecclesiae, prior et posterior.</li>
        <li>Potestate civili.</li>
      </ul>
    </li>
  </ul>
</section>

我希望这足以说明我想要实现的目标以及我遇到的问题。 <xsl:apply-templates select="." mode="listIsDone"/>不应该做我想做的事吗?

我无法找到任何搜索/谷歌搜索错误消息,这些错误信息与“空序列”,“铸造为'表达式”中的值或我未使用的xsl函数有关(在此处)。

我刚刚尝试了第二种方法call-template也没有产生正确的结果:

样式表:

<xsl:template name="list-heading">
    <xsl:param name="heading-node"/>
    <h4>
        <xsl:apply-templates select="$heading-node" mode="listIsDone"/>
    </h4>
</xsl:template>
...
<!-- and the in the matching template for "list", just like above, only at the for-each it is: -->
  <xsl:for-each select="child::head"> 
    <xsl:call-template name="list-heading">
      <xsl:with-param name="heading-node">
        <xsl:copy-of select="."/>
      </xsl:with-param>
    </xsl:call-template>
  </xsl:for-each>

这不会呈现标题的其他子元素(特别是<pb>)......

FWIW,我想在eXist中做这一切,我认为内部以某种方式使用Saxon。但是不要问我这些内部实现的细节,当我试图找到它们时,我对它的理解不够。 感谢您提供任何帮助

2 个答案:

答案 0 :(得分:1)

我不确定这是请求的输出还是可以在您的设置中使用,但只是想分享以下方法:

<?xml version="1.0"?><xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="2.0">
<xsl:output method="html"/> 
<xsl:template match="/*">
<html>
 <head></head>
 <body>
  <xsl:apply-templates select="list[not(child::list)]"/>
 </body>
 </html>
</xsl:template>  

<xsl:template match="list[not(child::list)]">
<section>
 <xsl:choose>
  <xsl:when test="child::head">
   <xsl:apply-templates select="child::head"/>
  </xsl:when>
  <xsl:otherwise>
   <h3>
    <xsl:text>No header for this list</xsl:text>
   </h3>
  </xsl:otherwise>
 </xsl:choose>
 <ul>
  <xsl:apply-templates select="child::item/list" mode="list"/>
 </ul>
</section>
</xsl:template>  

<xsl:template match="head" >
<h4>
 <xsl:apply-templates mode="header"/>
</h4>
</xsl:template>

<xsl:template match="pb" mode="header"/>
<xsl:template match="lb" mode="header"/>

<xsl:template match="list" mode="list">
<xsl:for-each select="./item">
 <xsl:apply-templates select="." mode="list"/>
</xsl:for-each>
</xsl:template>  

<xsl:template match="item" mode="list">
<li>
 <xsl:apply-templates mode="listItem"/>
</li>
</xsl:template>  

<xsl:template match="item" mode="listItem"/>
</xsl:stylesheet>  

为了进行测试,我刚刚在没有标题的XML示例中添加了第二个列表:

 <list>
  <item>
   <lb n="2"/>De
   <list>
    <item><lb n="3"/>Potestate ecclesiae, prior et posterior.</item>
    <item><lb n="4"/>Potestate civili.</item>
   </list>
  </item>
 </list>  

结果:

<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 </head>
 <body>
  <section>
   <h4>
     PRIMUS TOMUS,
   </h4>
   <ul>
     <li>Potestate ecclesiae, prior et posterior.</li>
     <li>Potestate civili.</li>
   </ul>
  </section>
  <section>
   <h3>No header for this list</h3>
   <ul>
     <li>Potestate ecclesiae, prior et posterior.</li>
     <li>Potestate civili.</li>
   </ul>
  </section>
 </body>
</html>  

由于我不知道在标题丢失的情况下应该是什么标题,我只是添加了一些默认副本;由于我不知道您希望如何处理<pb><lb>,因此他们只是匹配一个可以针对所需输出进行调整的空模板。

更新:在OP中添加所需的HTML输出之前发布了上述方法。我会稍后调整它以符合要求,因为我现在正在工作。建议解决方案的一个主要区别就是从每个外部列表条目开始,并从上到下处理缺少的标题,而不是从内部列表开始并从那里检查标题。由于在第一个模板match="list[not(child::list)]"中提供了生成单独部分输出所需的所有条目,我认为从那里开始更容易。

更新2:调整后的模板以匹配所需的输出,但仍有一些问题。

<?xml version="1.0"?>  
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="2.0">
<xsl:output method="html"/> 
<xsl:template match="/*">
<html>
 <head></head>
 <body>
  <xsl:apply-templates select="list[not(child::list)]"/>
 </body>
</html>
</xsl:template>  

<xsl:template match="list[not(child::list)]">
<section>
 <xsl:choose>
  <xsl:when test="child::head">
   <xsl:apply-templates select="child::head"/>
  </xsl:when>
  <xsl:otherwise>
   <h3>
    <xsl:text>No header for this list</xsl:text>
   </h3>
  </xsl:otherwise>
 </xsl:choose>
 <ul>
  <li>
   <xsl:apply-templates select="child::item" mode="listItem"/>
   <xsl:apply-templates select="child::item/list" mode="list"/>
  </li>
 </ul>
 </section>
</xsl:template>  

<xsl:template match="head" >
<h4>
 <xsl:apply-templates mode="header"/>
</h4>
</xsl:template>

<xsl:template match="pb" mode="header">
<xsl:variable name="facs" select="./@facs"/>
<xsl:variable name="pageNo" select="./@n"/>
<xsl:variable name="pageNoResolved" select="translate($pageNo, '[]', '')"/>
<!-- tbd: get href from facs with additional function -->
<!-- tbd: get pageNo from pageNo with additional function -->
<div class="pageNumbers">
 <a class="pageNo">
  <xsl:attribute name="href" select="$facs"/>
  <xsl:value-of select="$pageNoResolved"/>
 </a>
</div>
</xsl:template>

<xsl:template match="lb" mode="header"/>

<xsl:template match="list" mode="list">
<ul>
 <xsl:for-each select="./item">
  <xsl:apply-templates select="." mode="list"/>
 </xsl:for-each>
</ul>
</xsl:template>  

<xsl:template match="item" mode="list">
<li>
 <xsl:apply-templates mode="listItem"/>
</li>
</xsl:template>  

<xsl:template match="item" mode="listItem">
 <xsl:apply-templates mode="cleaning"/>
</xsl:template>

<xsl:template match="lb" mode="cleaning"/>
<xsl:template match="item" mode="cleaning"/>
</xsl:stylesheet>  

HTML输出:

<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 </head>
 <body>
  <section>
     <h4>
       <div class="pageNumbers">
          <a class="pageNo" href="facs:W0013-0005">2</a>
       </div>
        PRIMUS TOMUS,
     </h4>
     <ul>
        <li>
           De
           <ul>
              <li>Potestate ecclesiae, prior et posterior.</li>
              <li>Potestate civili.</li>
           </ul>
        </li>
     </ul>
  </section>
 </body>
</html>  

剩下的问题是:是否总会有&#34; | &#34;在<h4><div>之间?由于我不知道这是否可以改变,我只是将其添加为静态文本。因为我不知道如何检索href和pagenumber - 正如你提到的href将从facs - 属性中检索 - 我只是添加了值。如果pagenumber只是n="[2]" - pb属性的值,我只是删除了括号。

答案 1 :(得分:0)

事实证明最初的方法是正确的

错误是在某个函数中生成的,该函数在该过程的后期很晚才在模板中调用。因此,当我使用apply-templates处理列表标题时触发了它,并且当我使用value-of或切换模式而没有调整所有其他匹配模板再次启动时时触发它。

错误(由xs:string($nodeA@xml:id)实际上是几个节点时使用nodeA导致)导致修复后,我可以使用此样式表来实现我想要的内容:

<xsl:template match="head[not(parent::list)]">
    <h3>
        <xsl:apply-templates/>
    </h3>
</xsl:template>

<xsl:template match="list">
    <section>
        <xsl:for-each select="child::head">
            <h4>
                <xsl:apply-templates />
            </h4>
        </xsl:for-each>
        <xsl:for-each select="node()[not(local-name()='head')]">
            <ul>
                <xsl:apply-templates />
            </ul>
        </xsl:for-each>
    </section>
</xsl:template>

如果我没有忽略某些东西,这是一种更好的方法,因为它根本不使用模式(复杂性较低),不需要重复定义模板(列表的内部和外部),并且是关于级别的通用嵌套列表。

当然,完全错误地猜到了发生实际错误的地方,从而向错误的方向提出要求,这是非常尴尬的。 (好吧,至少我确实提到了错误信息,但是在我发布的样式表片段之外,显然没有帮助我或其他任何人发现错误。)

所以我真诚地道歉,因为事实上问题就在其他地方,所以你会花费这么多的努力。