【问题标题】:xslt php select the first sibling and all its child nodesxslt php 选择第一个兄弟节点及其所有子节点
【发布时间】:2010-02-11 14:29:50
【问题描述】:

如何选择 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 来完成,那就太好了。

【问题讨论】:

  • 在输入中进行合理/正确的嵌套现在会有很多的帮助。 &lt;div class="chapter"&gt; 不包含属于它的所有内容是否有更深层次的原因?
  • 我知道,主要原因是它在客户端部分更容易,这是一件好事(tm);)但是,这种方式有多容易,这里有一些内部辩论,必须承认我更喜欢更好的嵌套。
  • not 如何在客户端更轻松地将一堆逻辑单元包装在自己的 div 中?这可能会破坏什么?
  • 客户端作为实际提供原始输入的人。由于他们是高薪律师,他们在所见即所得编辑器中所做的工作越少越好。虽然如前所述,我对这种方式有多容易持怀疑态度......

标签: php xml xslt xpath


【解决方案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>

【讨论】:

    【解决方案2】:

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

    <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>
    

    【讨论】:

    • 这是有缺陷的,请用我使用的XML测试一下。
    • @Tomalak:哇哦。你当然是对的。我进行了改进(借用您的答案),正确处理包含多个章节、文章和段落的全套输入​​(就像您的示例输入一样),而不仅仅是问题中的有限示例,并对 XHTML 命名空间进行了调整。
    • 非常感谢你们!现在我要做的就是将它与我​​已经拥有的 xslt 合并,再次感谢。
    • 嗯,这有点太快了 :( 第一个示例(Tomalak)似乎有效,但只是部分有效(段落和 ul 中的大部分文本都丢失了),第二个示例大多没有做任何事情(似乎只有章节和标题匹配):(我必须承认我的例子可能有点过于简单,因为真正的 xhtml 包含更多不同的元素,但我只是想提出我遇到的基本问题/问题.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多