【问题标题】:XSLT split a tree at a descendent nodeXSLT 在后代节点处拆分树
【发布时间】:2011-02-26 19:24:12
【问题描述】:

我正在尝试根据后代元素的位置拆分元素树。 (特别是,我正在尝试解析 Adob​​e 的 IDML。)我希望能够转换如下所示的树:

<ParagraphStyleRange style="foo">
 <CharacterStyleRange style="bar">
  <Content>foo</Content>
  <Br />
  <Content>bar</Content>
 </CharacterStyleRange>
 <CharacterStyleRange style="bop">
  <Content>baz</Content>
  <Br />
  <Hyperlink>
   <Content>boo</Content>
    <Br />
   <Content>meep</Content>
  </Hyperlink>
</ParagraphStyleRange>

分裂成树:

<ParagraphStyleRange style="foo">
 <CharacterStyleRange style="bar">
  <Content>foo</Content>
 </CharacterStyleRange>
</ParagraphStyleRange>

<ParagraphStyleRange style="foo">
 <CharacterStyleRange style="bar">
  <Content>bar</Content>
 </CharacterStyleRange>
 <CharacterStyleRange style="bop">
  <Content>baz</Content>
 </CharacterStyleRange>
</ParagraphStyleRange>

<ParagraphStyleRange style="foo">
 <CharacterStyleRange style="bop">
  <Hyperlink>
   <Content>boo</Content>
  </Hyperlink>
 </CharacterStyleRange>
</ParagraphStyleRange>

<ParagraphStyleRange style="foo">
 <CharacterStyleRange style="bop">
  <Hyperlink>
   <Content>meep</Content>
  </Hyperlink>
 </CharacterStyleRange>
</ParagraphStyleRange>

然后我可以使用普通的 XSL 对其进行解析。 (编辑:我最初在其原始位置显示了&lt;Br/&gt; 标签,但它们是否存在并不重要,因为它们包含的信息现在由拆分元素表示。我认为这可能更容易解决这个问题,不用担心把它们留在里面。)

我尝试按照 XSLT 2.0 规范中的建议使用xsl:for-each-group(例如&lt;xsl:for-each-group select="CharacterStyleRange/*" group-ending-with="Br"&gt;),但我不知道如何在树的每个级别应用它(&lt;Br /&gt; 标签可以出现在任何级别,例如在 &lt;CharacterStyleRange&gt; 元素内的 &lt;Hyperlink&gt; 元素内,这也限制了我只能使用适用于所选深度的模板。

编辑:我的示例代码只显示了一个需要拆分树的地方,但可以有任意数量的拆分点(尽管总是相同的元素。)

编辑 2:我添加了一些更详细的示例,以显示一些复杂性。

【问题讨论】:

标签: xslt


【解决方案1】:

这个 XSLT 1.0(当然还有 XSLT 2.0)转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
       <xsl:output omit-xml-declaration="yes" indent="yes"/>
       <xsl:strip-space elements="*"/>

       <xsl:template match="text()"/>

       <xsl:template match="/">
         <xsl:call-template name="Split">
          <xsl:with-param name="pSplitters"
           select="//Br"/>
         </xsl:call-template>
       </xsl:template>

       <xsl:template name="Split">
         <xsl:param name="pSplitters"/>

         <xsl:if test="$pSplitters">
           <xsl:for-each select="$pSplitters[1]">

             <xsl:call-template name="buildTree">
              <xsl:with-param name="pLeafs" select=
              "preceding-sibling::node()[not(descendant::Br)]"/>
          </xsl:call-template>

          <xsl:if test=
            "not(following-sibling::node()//Br)">
                 <xsl:call-template name="buildTree">
                  <xsl:with-param name="pLeafs" select=
                  "following-sibling::node()"/>
                 </xsl:call-template>
          </xsl:if>

          <xsl:call-template name="Split">
            <xsl:with-param name="pSplitters" select=
             "$pSplitters[position() > 1]"/>
          </xsl:call-template>
          </xsl:for-each>
         </xsl:if>
       </xsl:template>

 <xsl:template name="buildTree">
  <xsl:param name="pAncestors" select="ancestor::*"/>
  <xsl:param name="pLeafs"/>

  <xsl:choose>
    <xsl:when test="not($pAncestors)">
     <xsl:copy-of select="$pLeafs"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="vtopAncestor" select="$pAncestors[1]"/>

      <xsl:element name="{name($vtopAncestor)}"
           namespace="{namespace-uri($vtopAncestor)}">
        <xsl:copy-of select=
             "$vtopAncestor/namespace::* | $vtopAncestor/@*"/>
        <xsl:call-template name="buildTree">
          <xsl:with-param name="pAncestors"
               select="$pAncestors[position()>1]"/>
          <xsl:with-param name="pLeafs" select="$pLeafs"/>
        </xsl:call-template>
      </xsl:element>
     </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<ParagraphStyleRange style="foo">
    <CharacterStyleRange style="bar">
        <Content>foo</Content>
        <Br />
        <Content>bar</Content>
    </CharacterStyleRange>
    <CharacterStyleRange style="bop">
        <Content>baz</Content>
        <Br />
        <Hyperlink>
            <Content>boo</Content>
            <Br />
            <Content>meep</Content>
        </Hyperlink>
    </CharacterStyleRange>
</ParagraphStyleRange>

产生想要的正确结果

<ParagraphStyleRange style="foo">
   <CharacterStyleRange style="bar">
      <Content>foo</Content>
   </CharacterStyleRange>
</ParagraphStyleRange>
<ParagraphStyleRange style="foo">
   <CharacterStyleRange style="bar">
      <Content>bar</Content>
   </CharacterStyleRange>
</ParagraphStyleRange>
<ParagraphStyleRange style="foo">
   <CharacterStyleRange style="bop">
      <Content>baz</Content>
   </CharacterStyleRange>
</ParagraphStyleRange>
<ParagraphStyleRange style="foo">
   <CharacterStyleRange style="bop">
      <Hyperlink>
         <Content>boo</Content>
      </Hyperlink>
   </CharacterStyleRange>
</ParagraphStyleRange>
<ParagraphStyleRange style="foo">
   <CharacterStyleRange style="bop">
      <Hyperlink>
         <Content>meep</Content>
      </Hyperlink>
   </CharacterStyleRange>
</ParagraphStyleRange>

【讨论】:

  • 嗯,这似乎只能让您将树拆分到一个地方。我想我的问题并不清楚;可以有多个 &lt;Br&gt; 元素,每个元素都需要拆分树。
  • @Quentin Smith:那么你必须编辑你的问题并提供一个更具代表性的例子。
  • @lwburk:空行是为了提高可读性。至少这是我的偏好。当我得到某人的代码格式不完美时,我做的第一件事就是拆分长行并引入分隔空行,以便提高可读性。
  • @Dimitre - 很公平。我不认为空行对提高可读性有多大作用,但理性的人可能会不同意。
  • @lwburk,我同意 Dimitre... 我很欣赏他代码中的空白行。我也把它们放在我自己的里面。让这些老眼睛更容易
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 2015-06-25
  • 1970-01-01
  • 1970-01-01
  • 2016-10-25
  • 1970-01-01
相关资源
最近更新 更多