xslt php选择第一个兄弟节点及其所有子节点

时间:2010-02-11 14:29:50

标签: php xml xslt xpath

如何选择xml节点的第一个兄弟节点及其所有子节点并对其应用一些转换?到目前为止,我只成功选择了第一个兄弟节点和第一个兄弟节点(没有子节点)或xml节点之后的所有内容。

假设我们有这样的xhtml:

<div class="chapter">Chapter <span class="number">1.1</span> Lorum ipsum</div>
<h2 class="article">Article <span class="number">1.</span> Lorum ipsum</h2>
<p>Lorum ipsum</p>

我们追求的结果是像这样的xml:

<chapter>
  <heading>
    <label>Chapter</chapter>
    <number>1.1</number>
    <title>Lorum ipsum</title>
  </heading>
  <article>
    <heading>
      <label>Article</chapter>
      <number>1.</number>
      <title>Lorum ipsum</title>
    </heading>
    <par>Lorum ipsum</par>
  </article>
</chapter>

我的猜测是我需要做一些正则表达式魔法来正确获取标签和标题标签,但如果使用普通的xslt也可以做到这一点很棒。

2 个答案:

答案 0 :(得分:1)

此XSLT 1.0转换:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xhtml="http://www.w3.org/1999/xhtml"
  exclude-result-prefixes="xhtml"
>
  <xsl:output encoding="utf-8" />

  <!-- the identity template (copies all nodes verbatim, unless
       more specific templates implement different behavior) -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>

  <!-- start by applying templates to all chapter divs -->
  <xsl:template match="xhtml:html">
    <text>
      <xsl:apply-templates select="xhtml:body/xhtml:div[@class='chapter']" />
    </text>
  </xsl:template>

  <!-- chapter div: generate heading, apply templates to adjacent h2 -->
  <xsl:template match="xhtml:div[@class='chapter']">
    <chapter>
      <xsl:apply-templates select="." mode="heading" />
      <!-- ... where the first preceding chapter div has the same ID -->
      <xsl:apply-templates select="
        following-sibling::xhtml:h2[
          generate-id(preceding-sibling::xhtml:div[@class='chapter'][1])
          =
          generate-id(current())
        ]
      "/>
    </chapter>
  </xsl:template>

  <!-- h2: generate heading, apply templates to adjacent paras -->
  <xsl:template match="xhtml:h2[@class='article']">
    <article>
      <xsl:apply-templates select="." mode="heading" />
      <xsl:apply-templates select="
        following-sibling::xhtml:p[
          generate-id(preceding-sibling::xhtml:h2[@class='article'][1])
          =
          generate-id(current())
        ]
      "/>
    </article>
  </xsl:template>

  <!-- headings follow the same scheme, so we can use a unified template -->
  <xsl:template match="xhtml:div | xhtml:h2" mode="heading">
    <heading>
      <label>
        <xsl:value-of select="normalize-space(text()[1])" />
      </label>
      <number>
        <xsl:value-of select="normalize-space(xhtml:span[@class='number'])" />
      </number>
      <title>
        <xsl:value-of select="normalize-space(text()[2])" />
      </title>
    </heading>
  </xsl:template>

  <xsl:template match="xhtml:p">
    <par>
      <xsl:apply-templates select="node()" />
    </par>
  </xsl:template>

</xsl:stylesheet>

应用于

<html xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <div class="chapter">Chapter <span class="number">1.1</span> Lorum ipsum</div>
    <h2 class="article">Article <span class="number">1.</span> Lorum ipsum</h2>
    <p>Lorum ipsum A</p>
    <p>Lorum ipsum B</p>
    <h2 class="article">Article <span class="number">2.</span> Lorum ipsum</h2>
    <p>Lorum ipsum D</p>
    <h2 class="article">Article <span class="number">3.</span> Lorum ipsum</h2>
    <p>Lorum ipsum E</p>
    <p>Lorum ipsum F</p>
    <div class="chapter">Chapter <span class="number">2.1</span> Lorum ipsum</div>
    <h2 class="article">Article <span class="number">1.</span> Lorum ipsum</h2>
    <p>Lorum ipsum G</p>
  </body>
</html>

的产率:

<text>
  <chapter>
    <heading>
      <label>Chapter</label>
      <number>1.1</number>
      <title>Lorum ipsum</title>
    </heading>
    <article>
      <heading>
        <label>Article</label>
        <number>1.</number>
        <title>Lorum ipsum</title>
      </heading>
      <par>Lorum ipsum A</par>
      <par>Lorum ipsum B</par>
    </article>
    <article>
      <heading>
        <label>Article</label>
        <number>2.</number>
        <title>Lorum ipsum</title>
      </heading>
      <par>Lorum ipsum D</par>
    </article>
    <article>
      <heading>
        <label>Article</label>
        <number>3.</number>
        <title>Lorum ipsum</title>
      </heading>
      <par>Lorum ipsum E</par>
      <par>Lorum ipsum F</par>
    </article>
  </chapter>
  <chapter>
    <heading>
      <label>Chapter</label>
      <number>2.1</number>
      <title>Lorum ipsum</title>
    </heading>
    <article>
      <heading>
        <label>Article</label>
        <number>1.</number>
        <title>Lorum ipsum</title>
      </heading>
      <par>Lorum ipsum G</par>
    </article>
  </chapter>
</text>

答案 1 :(得分:1)

此样式表可创建所需的输出:

     

                 

<xsl:template match="html:div[@class='chapter']" mode="chapter">
    <xsl:element name="{@class}">
        <heading>
            <xsl:apply-templates mode="chapter" />
        </heading>
        <xsl:apply-templates select="following-sibling::html:h2[generate-id(preceding-sibling::html:div[@class='chapter'][1])=generate-id(current())]" mode="chapter" />
    </xsl:element>
</xsl:template>

<!--template for h2 in "chapter" mode, creates article content for the chapter-->
<xsl:template match="html:h2[@class='article']" mode="chapter">
    <xsl:element name="{@class}">
        <heading>
            <xsl:apply-templates mode="chapter"/>
        </heading>
        <xsl:apply-templates select="following-sibling::html:p[generate-id(preceding-sibling::html:h2[@class='article'][1])=generate-id(current())]" mode="chapter" />
    </xsl:element>
</xsl:template>

<xsl:template match="text()[following-sibling::html:span[@class='number']]" mode="chapter">
    <label><xsl:value-of select="normalize-space()"/></label>
</xsl:template>

<!--Generate an (number) element using the class attribute as the name of the element-->
<xsl:template match="html:span[@class='number']" mode="chapter">
    <xsl:element name="{@class}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

<!--title elements created for text nodes before the -->
<xsl:template match="text()[preceding-sibling::html:span[@class='number']]" mode="chapter">
    <title><xsl:value-of select="normalize-space()"/></title>
</xsl:template>

<!--Template in "chapter" mode, creates a par element inside the article-->
<xsl:template match="html:p" mode="chapter">
    <para><xsl:value-of select="normalize-space()"/></para>
</xsl:template>

<!--prevent text from bleeding through in output-->
<xsl:template match="text()" mode="chapter"/>

使用Tomalak的示例输入XML,生成:

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <chapter>
        <heading>
            <label>Chapter</label>
            <number>1.1</number>
            <title>Lorum ipsum</title>
        </heading>
        <article>
            <heading>
                <label>Article</label>
                <number>1.</number>
                <title>Lorum ipsum</title>
            </heading>
            <para>Lorum ipsum A</para>
            <para>Lorum ipsum B</para>
        </article>
        <article>
            <heading>
                <label>Article</label>
                <number>2.</number>
                <title>Lorum ipsum</title>
            </heading>
            <para>Lorum ipsum D</para>
        </article>
        <article>
            <heading>
                <label>Article</label>
                <number>3.</number>
                <title>Lorum ipsum</title>
            </heading>
            <para>Lorum ipsum E</para>
            <para>Lorum ipsum F</para>
        </article>
    </chapter>
    <chapter>
        <heading>
            <label>Chapter</label>
            <number>2.1</number>
            <title>Lorum ipsum</title>
        </heading>
        <article>
            <heading>
                <label>Article</label>
                <number>1.</number>
                <title>Lorum ipsum</title>
            </heading>
            <para>Lorum ipsum G</para>
        </article>
    </chapter>
</book>