如何构造XSL以从XML创建HTML

时间:2013-12-14 20:05:47

标签: xml xslt

我有一本书的XML文档。每个XML文档都是一章,按以下方式构建:

<doc>
<chapter title="This is the first chapter">
<section>This is a section of information</section>
<paragraph title="This is the first paragraph">This is the text of the first paragraph</paragraph>
<section><paragraph title="This is the second paragraph">This paragraph is inside a secion</paragraph></section>
</chapter>
</doc>

我有一个PHP从XSL生成HTML,但我的问题是以一种好的方式构建XSL。如你所见,段落不一定是章节的第一个孩子。一个部分可以包含一个段落,但不必包含。

如何在XSL中轻松构建它?我是否必须嵌套一堆foreach标签?我试过这样的事情:

<xsl:for-each select="doc/chapter">
                    <h1>
                        <xsl:value-of select="current()/@title"/>
                    </h1>

                <xsl:for-each select="current()/paragraph">
                        <h3>
                            <xsl:value-of select="current()/@title" />
                        </h3>

                            <p><xsl:value-of select="current()" /></p>
        </xsl:for-each>

                <xsl:for-each select="current()/section">
                        <h2>
                            <xsl:value-of select="current()/@title" />
                        </h2>
                    <xsl:for-each select="current()/paragraph">
                            <h3>
                                <xsl:value-of select="current()/@title" />
                            </h3>

                                <p><xsl:value-of select="current()" /></p>
            </xsl:for-each>
        </xsl:for-each>
</xsl:for-each>

问题在于,这将是非常先进的,并且许多重复的foreach'彼此嵌套。是否有更简单,更好的方法。如果是这样,怎么样?

2 个答案:

答案 0 :(得分:3)

你需要在这里使用模板匹配,这正是XSLT的全部内容,并且擅长。如果您想以相同的方式转换段落元素,无论文档中的位置如何,您首先要创建一个描述如何转换它的模板

<xsl:template match="paragraph">
   <h3>
      <xsl:value-of select="current()/@title"/>
   </h3>
   <p>
     <xsl:value-of select="current()"/>
    </p>
</xsl:template>

然后,您可以将其替换为 xsl:apply-templates

,而不是执行 xsl:for-each
<xsl:apply-templates select="paragraph" />

(注意,这里不需要使用“current()”,因为假设xpath表达式无论如何都是相对于当前上下文节点。)

但这仍然不理想,因为它假设你现在期待一个段落,当时可能还有其他元素。更好的是当你这样做时

<xsl:apply-templates />

这将处理当前元素的所有当前子节点,并查找匹配的模板。如果您有想要转换的所有元素的模板,这将很有效。

还值得一提的是XSLT的内置模板。这些将用于匹配XSLT中没有显式匹配模板的节点。这些内置模板将输出找到它们的文本节点,否则它们将通过查找与当前节点的子节点匹配的模板来继续处理。

您可以利用这些内置模板来简化XSLT。例如,这是编写它的一种方法

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="chapter">
         <h1>
            <xsl:value-of select="@title"/>
         </h1>
         <xsl:apply-templates />
   </xsl:template>

   <xsl:template match="paragraph">
      <h3>
         <xsl:value-of select="@title"/>
      </h3>
      <p>
         <xsl:value-of select="."/>
      </p>
   </xsl:template>
</xsl:stylesheet>

请注意,没有模板匹配 doc 部分,但内置模板将处理,允许执行最终传递给显式模板。

编辑:如果您确实想要进行更多更改,只需根据需要添加更多更改。例如,如果您的 Section 元素具有 title 属性,则只需添加以下模板:

   <xsl:template match="section[@title]">
      <h2>
         <xsl:value-of select="@title"/>
      </h2>
      <xsl:apply-templates />
   </xsl:template>

此模板将匹配部分元素,但仅包含 title 属性的元素。

答案 1 :(得分:1)

您是否考虑过切换到docbook格式?采用标准的优点是您可以利用预先存在的样式表来支持各种输出格式。

为了说明这里的一个示例文档:

<book>
  <title>An Example Book</title>
  <chapter>
    <title>This is the first chapter</title>
    <section>
      <title>This is the first section</title>
      <para>Paragraph one</para>
      <para>Paragraph two</para>
    </section>
    <section>
      <title>This is the second section</title>
      <para>Paragraph one</para>
      <para>Paragraph two</para>
    </section>
  </chapter>
</book>