XSLT嵌套列表分组错误

时间:2017-07-10 21:45:15

标签: xml list xslt nested grouping

我的XSLT以错误的方式将我的嵌套列表分组。期望的输出如下。它将所有hanging_indents分组为两个ref-list。请帮忙。我下面的代码大部分都在下面。我的代码主要在下面。我的代码主要在下面。

输入XML第一种情况:

<document>
  <head1>Heading 1</head1>
  <body_text>foo text</body_text>
  <body_text>foo text</body_text>
  <head1>Heading 2</head1>
  <body_text>foo text</body_text>

   <head1>Bibliography</head1>
        <bib_head>Books</bib_head>
            <hanging_indent>book citation 1</hanging_indent>
            <hanging_indent>book citation 2</hanging_indent>
        <bib_head>Periodicals</bib_head>
            <hanging_indent>per citation 1</hanging_indent>
            <hanging_indent>per citation 2</hanging_indent>
</document>

输入XML第二种情况(无bib_heads):

<document>
  <head1>Heading 1</head1>
  <body_text>foo text</body_text>
  <body_text>foo text</body_text>
  <head1>Heading 2</head1>
  <body_text>foo text</body_text>

   <head1>Bibliography</head1>
            <hanging_indent>book citation 1</hanging_indent>
            <hanging_indent>book citation 2</hanging_indent>
            <hanging_indent>per citation 1</hanging_indent>
            <hanging_indent>per citation 2</hanging_indent>
</document>

我的XSLT:

<xsl:template match="head1">        
    <xsl:variable name="head1" select="." />

<xsl:if test="name(following-sibling::*[1])='body_text'">
        <sec sec-type="bodytext" indexed="true">
            <title><xsl:value-of select="."/></title>
            <xsl:apply-templates select="following-sibling::body_text[preceding-sibling::head1[1] = $head1]" />
        </sec>
    </xsl:if>


  <xsl:if test="name(following-sibling::*[1])='hanging_indent'">
        <sec sec-type="hangindent" indexed="true">
            <title><xsl:value-of select="."/></title>
            <ref-list>
                <xsl:apply-templates select="following-sibling::hanging_indent[preceding-sibling::head1[1] = $head1]" />
            </ref-list>
        </sec>
    </xsl:if>
    <xsl:if test="name(following-sibling::*[1])='bib_head'">
        <sec sec-type="hangindent" indexed="true">
            <title><xsl:value-of select="."/></title>
            <xsl:apply-templates select="following-sibling::bib_head"/>
        </sec>
    </xsl:if>
</xsl:template><xsl:template match="hanging_indent">
    <ref>
        <citation>
            <xsl:apply-templates select="node()"/>
        </citation>
    </ref>
</xsl:template><xsl:template match="bib_head">
    <ref-list>
        <title>
            <xsl:apply-templates select="node()"/>
        </title>
        <xsl:apply-templates select="following-sibling::hanging_indent"/>
    </ref-list>
</xsl:template>

所需的XML输出结构:

<sec sec-type="bodytext" indexed="true">
  <title>Heading 1</title>
  <body_text>foo text</body_text>
  <body_text>foo text</body_text>
</sec>
<sec sec-type="bodytext" indexed="true">
  <title>Heading 2</title>
  <body_text>foo text</body_text>
</sec>
<sec sec-type="hangindent" indexed="true">
  <title>Bibliography</title>
  <ref-list>
    <title>Books</title>
    <ref><citation>book citation 1</citation></ref>
    <ref><citation>book citation 2</citation></ref>
  </ref-list>
  <ref-list>
    <title>Periodicals</title>
    <ref><citation>per citation 1</citation></ref>
    <ref><citation>per citation 2</citation></ref>
  </ref-list>
</sec>

3 个答案:

答案 0 :(得分:0)

也许试试:

  • 按照前一个兄弟hanging_indent的生成ID对bib_head个元素进行分组。
  • 按照前一个兄弟bib_head的生成ID对head1个元素进行分组。
  • 按照前一个兄弟body_text的生成ID对head1个元素进行分组。
  • 按照第一个前一个兄弟hanging_indent的生成ID对head1个元素进行分组。 (如果没有bib_head元素,则使用。)

场景#1 XML输入(缩进更改以减少有关嵌套的混淆)

<document>
    <head1>Heading 1</head1>
    <body_text>foo text</body_text>
    <body_text>foo text</body_text>
    <head1>Heading 2</head1>
    <body_text>foo text</body_text>
    <head1>Bibliography</head1>
    <bib_head>Books</bib_head>
    <hanging_indent>book citation 1</hanging_indent>
    <hanging_indent>book citation 2</hanging_indent>
    <bib_head>Periodicals</bib_head>
    <hanging_indent>per citation 1</hanging_indent>
    <hanging_indent>per citation 2</hanging_indent>
</document>

场景#2 XML输入(无bib_head)(缩进更改以减少有关嵌套的混淆)

<document>
    <head1>Heading 1</head1>
    <body_text>foo text</body_text>
    <body_text>foo text</body_text>
    <head1>Heading 2</head1>
    <body_text>foo text</body_text>
    <head1>Bibliography</head1>
    <hanging_indent>book citation 1</hanging_indent>
    <hanging_indent>book citation 2</hanging_indent>
    <hanging_indent>per citation 1</hanging_indent>
    <hanging_indent>per citation 2</hanging_indent>
</document>

XSLT 1.0

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

  <!--Group hanging_indent by first preceding sibling bib_head.-->
  <xsl:key name="hi-by-bh" 
    match="hanging_indent" 
    use="generate-id(preceding-sibling::bib_head[1])"/>
  <!--Group bib_head by first preceding head1.-->
  <xsl:key name="bh-by-h1" 
    match="bib_head" 
    use="generate-id(preceding-sibling::head1[1])"/>
  <!--Group body_text by first preceding head1.-->
  <xsl:key name="bt-by-h1" 
    match="body_text" 
    use="generate-id(preceding-sibling::head1[1])"/>
  <!--Group hanging_indent by first preceding head1.-->
  <xsl:key name="hi-by-h1" 
    match="hanging_indent" 
    use="generate-id(preceding-sibling::head1[1])"/>

  <xsl:template match="/*">
    <xsl:apply-templates select="head1"/>
  </xsl:template>

  <xsl:template match="head1[following-sibling::*[1][self::body_text]]">
    <sec sec-type="bodytext" indexed="true">
      <title><xsl:value-of select="."/></title>
      <xsl:apply-templates select="key('bt-by-h1',generate-id())"/>
    </sec>
  </xsl:template>

  <xsl:template match="head1[following-sibling::*[1][self::bib_head]]">
    <sec sec-type="hangindent" indexed="true">
      <title><xsl:value-of select="."/></title>
      <xsl:apply-templates select="key('bh-by-h1',generate-id())"/>
    </sec>
  </xsl:template>

  <xsl:template match="head1[following-sibling::*[1][self::hanging_indent]]">
    <sec sec-type="hangindent" indexed="true">
      <title><xsl:value-of select="."/></title>
      <xsl:apply-templates select="key('hi-by-h1',generate-id())"/>
    </sec>
  </xsl:template>

  <xsl:template match="bib_head">
    <ref-list>
      <title><xsl:value-of select="."/></title>
      <xsl:apply-templates select="key('hi-by-bh',generate-id())"/>
    </ref-list>
  </xsl:template>

  <xsl:template match="hanging_indent">
    <ref><citation><xsl:value-of select="."/></citation></ref>
  </xsl:template>

  <xsl:template match="body_text">
    <xsl:copy-of select="."/>
  </xsl:template>

</xsl:stylesheet>

输出方案#1

<sec sec-type="bodytext" indexed="true">
   <title>Heading 1</title>
   <body_text>foo text</body_text>
   <body_text>foo text</body_text>
</sec>
<sec sec-type="bodytext" indexed="true">
   <title>Heading 2</title>
   <body_text>foo text</body_text>
</sec>
<sec sec-type="hangindent" indexed="true">
   <title>Bibliography</title>
   <ref-list>
      <title>Books</title>
      <ref>
         <citation>book citation 1</citation>
      </ref>
      <ref>
         <citation>book citation 2</citation>
      </ref>
   </ref-list>
   <ref-list>
      <title>Periodicals</title>
      <ref>
         <citation>per citation 1</citation>
      </ref>
      <ref>
         <citation>per citation 2</citation>
      </ref>
   </ref-list>
</sec>

输出方案#2

<sec sec-type="bodytext" indexed="true">
   <title>Heading 1</title>
   <body_text>foo text</body_text>
   <body_text>foo text</body_text>
</sec>
<sec sec-type="bodytext" indexed="true">
   <title>Heading 2</title>
   <body_text>foo text</body_text>
</sec>
<sec sec-type="hangindent" indexed="true">
   <title>Bibliography</title>
   <ref>
      <citation>book citation 1</citation>
   </ref>
   <ref>
      <citation>book citation 2</citation>
   </ref>
   <ref>
      <citation>per citation 1</citation>
   </ref>
   <ref>
      <citation>per citation 2</citation>
   </ref>
</sec>

答案 1 :(得分:0)

由于您没有指定XSLT版本,我选择版本2.0, 它提供了一种简单而自然的分组元素的方法。

任务归结为2个嵌入式for-each-group循环。 外部循环从head1元素开始创建每个组 和内循环从bib_head开始。

以下是您可以使用的脚本:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="document">
    <xsl:copy>
    <xsl:for-each-group select="*" group-starting-with="head1">
      <xsl:variable name="type">
        <xsl:choose>
          <xsl:when test="current-group()[2][name()='body_text']">
            <xsl:text>bodytext</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:text>hangindent</xsl:text>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
      <xsl:variable name="group" select="current-group()"/>
      <xsl:element name="sec">
        <xsl:attribute name="sec-type" select="$type"/>
        <xsl:attribute name="indexed" select="'true'"/>
        <title>
          <xsl:value-of select="$group[name()='head1']"/>
        </title>
        <xsl:choose>
          <xsl:when test="$type='bodytext'">
            <xsl:copy-of select="$group[name()='body_text']"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="BibHead">
              <xsl:with-param name="group" select="subsequence($group, 2)"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:element>
    </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

  <!-- Generate ref-list elements -->
  <!-- group - source group without head1 -->
  <xsl:template name="BibHead">
    <xsl:param name="group"/>
    <xsl:for-each-group select="$group" group-starting-with="bib_head">
      <ref-list>
        <title><xsl:value-of select="current-group()[name()='bib_head']"/></title>
        <xsl:for-each select="subsequence(current-group(), 2)">
          <ref><citation><xsl:value-of select="."/></citation></ref>
        </xsl:for-each>
      </ref-list>
    </xsl:for-each-group>
  </xsl:template>

</xsl:transform>

答案 2 :(得分:0)

假设XSLT 2.0或3.0你可以简单地嵌套两个xsl:for-each-group group-starting-with,所以使用Saxon 9.8或当前的Altova你可以使用

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:param name="STREAMABLE" static="yes" as="xs:boolean" select="true()"/>

    <xsl:mode _streamable="{$STREAMABLE}" on-no-match="shallow-copy"/>

    <xsl:output indent="yes"/>

    <xsl:template match="document">
        <xsl:copy>
            <xsl:for-each-group select="*" group-starting-with="head1">
                <sec sec-type="body-text" indexed="true">
                    <xsl:for-each-group select="current-group()" group-starting-with="bib_head">
                        <xsl:choose>
                            <xsl:when test="self::bib_head">
                                <ref-list>
                                    <xsl:apply-templates select="current-group()"/>
                                </ref-list>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:apply-templates select="current-group()"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each-group>
                </sec>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="head1 | bib_head">
        <title>
            <xsl:apply-templates/>
        </title>
    </xsl:template>

    <xsl:template match="hanging_indent">
        <ref>
            <citation>
                <xsl:apply-templates/>
            </citation>
        </ref>
    </xsl:template>

</xsl:stylesheet>

使用XSLT 2.0只需删除

    <xsl:param name="STREAMABLE" static="yes" as="xs:boolean" select="true()"/>

并替换

    <xsl:mode _streamable="{$STREAMABLE}" on-no-match="shallow-copy"/>

使用身份转换模板

<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>