动态显示xml节点并使用递归将样式应用于特定节点

时间:2014-03-04 08:44:18

标签: xslt xpath xsl-fo

下面是我的xml文件。

<xml>
<top>
 <main>
    <firstname>John</firstname>
    <lastname>John</lastname>
    <table></table>
    <chapter>
       <firstname>Alex</firstname>
       <lastname>Robert</lastname>
       <p>Sample text chap</p>
       <figure name="f1.svg"></figure>
       <chapter>
          <firstname>Rebec</firstname>
          <lastname></lastname>
          <p>Sample text</p>
          <figure name="f2.svg"></figure>
       </chapter>
    </chapter>
 </main>
</top>
</xml>

期望的输出:

<bold>John
table
<bold>Robert
Sample text chap
f1.svg
<bold> Rebec
Sample text
f2.svg

解释:我写了一个xslt来做到这一点。我需要动态获取xml节点。我不能写:xsl:apply-templates select ='main / lastname'。因为我的xml格式可以随时更改。 我尝试过使用'$ root / *'首先获取所有xml节点的逻辑。然后,如果遇到'table'元素,我使用xsl:apply-templates select ='current()[name()='TABLE']'并执行表创建操作。 这很好用。我得到了所需的输出,但我的图形元素只在输出的每个位置显示f1.svg。 f2.svg未显示。 我如何只匹配'lastname'并使其变为粗体? 我想使代码尽可能通用/模块化,以便它遍历xml树的所有元素,并在特定节点上进行一些格式化。 下面是递归的xslt。有了这个,我的数据就会重复出现。我正在编写递归模板,因为xslt不是顺序的。

XSLT:

<xsl:call-template name="FetchNodes">
<xsl:with-param name="endIndex" select="$NumberOfNodes" />
<xsl:with-param name="startIndex" select="1" />
<xsl:with-param name="context" select="$root/*" />
</xsl:call-template>


<xsl:template name="FetchNodes">
    <xsl:param name="endIndex" />
    <xsl:param name="startIndex" />
    <xsl:param name="context" />
    <xsl:if test="$startIndex &lt;= $endIndex">
           <xsl:if test="$context[$startIndex][name() = 'table']"">
                  <xsl:apply-templates select="$context[$startIndex][name() = 'table']"" mode="table" />
      </xsl:if>
            <xsl:call-template name="FetchNodes">
        <xsl:with-param name="endIndex" select="$endIndex" />
        <xsl:with-param name="startIndex" select="$startIndex + 1"/>
        <xsl:with-param name="context" select="$context" />
    </xsl:call-template>
</xsl:if>
</xsl:template>

    <xsl:template match="node()" mode="table">   
       <xsl:value-of select="node()" />
    </xsl:template>

使用上面的xslt,应用模板的xpath中有些不正确。输出不正确。
我想要XSL FO输出。

有人能提出什么建议吗?

1 个答案:

答案 0 :(得分:1)

显示“f1.svg”而不是“f2.svg”的问题是因为这行

<xsl:variable name="ImageName">
    <xsl:value-of select="$root/*/chapter/figure/@name" />
</xsl:variable>

此时您已经定位在数字上,因此您只需要在此处使用相对xpath表达式。您当前使用的是绝对路径,因此无论您的上下文如何,它都将始终返回第一个 @name 属性。它应该看起来这个

<xsl:variable name="ImageName">
    <xsl:value-of select="@name" />
</xsl:variable>

或者更好,就像这样

<xsl:variable name="ImageName" select="@name" />

说完了,代码就在一个模板中,试图匹配一个 FIG 元素,这个元素在你向我们展示的XML中不存在。您实际上可以简化模板匹配,例如

 <xsl:template match="figure" mode="figure">

至于粗体,您只需将 font-weight 属性添加到要制作粗体的任何块中。像这样:

<xsl:choose>
   <xsl:when test="self::lastname">
      <fo:inline font-weight="bold"><xsl:value-of select="text()" /></fo:inline>
   </xsl:when>
   <xsl:otherwise>
      <xsl:value-of select="text()" />
   </xsl:otherwise>
</xsl:choose>

编辑:说了这么多,你可能没有采取正确的方法解决问题。使用模板匹配可能会更好,利用XSLT的内置模板来浏览文档。基本上,只需为要匹配的每个元素编写一个模板,然后生成输出,然后继续匹配其子元素。

例如,要将章节变为 fo:block ,请执行此操作

<xsl:template match="chapter">
   <fo:block>
      <xsl:apply-templates/>
  </fo:block>
</xsl:template>

要以粗体输出名字,请执行此操作

<xsl:template match="firstname">
   <fo:inline font-weight="bold">
      <xsl:value-of select="text()"/>
   </fo:inline>
</xsl:template>

要将数字转换为图像,请执行此操作(请注意,此处使用属性值模板,花括号表示要计算的表达式,而不是按字面输出)

<xsl:template match="figure">
   <fo:block>
      <fo:external-graphic src="../resources/{@name}" content-height="60%" scaling="uniform" padding-left="2cm"/>
   </fo:block>
</xsl:template>

尝试将此XSLT作为起点,并在其上构建

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="main">
      <fo:block>
         <xsl:apply-templates/>
      </fo:block>
   </xsl:template>

   <xsl:template match="chapter">
      <fo:block>
         <xsl:apply-templates/>
      </fo:block>
   </xsl:template>

   <xsl:template match="firstname">
      <fo:inline font-weight="bold">
         <xsl:value-of select="text()"/>
      </fo:inline>
   </xsl:template>

   <xsl:template match="lastname"/>

   <xsl:template match="figure">
      <fo:block>
         <fo:external-graphic src="../resources/{@name}" content-height="60%" scaling="uniform" padding-left="2cm"/>
      </fo:block>
   </xsl:template>
</xsl:stylesheet>