无法使用分组执行XSLT转换 [英] Not able to do XSLT transformation with grouping

查看:19
本文介绍了无法使用分组执行XSLT转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类似的XML,我想将下面的XML转换成不同的格式,但是不能得到确切的输出,但是可以得到输出的一部分

<?xml version="1.0" encoding="UTF-8"?>
<text width="12240" height="15840" orient="">
  <p style="TRH2">
    <r>Partial Interests</r>
  </p>
  <p style="TRH4">
    <r>5.01Concurrent Interests</r>
  </p>
  <p style="TRH5">
    <r>[1]In General</r>
  </p>
  <p style="TRH6">
    <r>Arabic</r>
  </p>
  <p style="TRH7">
    <r>section</r>
  </p>
  <p style="TRNormal">
    <r>An owner.</r>
  </p>
  <p style="TRNormal">
    <r>On other</r>
  </p>
  <p/>
  <p style="TRH7">
    <r>sections</r>
  </p>
  <p style="TRNormal">
    <r>entitled</r>
  </p>
  <p style="TRNormal">
    <r>in which</r>
  </p>
  <p style="TRH4">
    <r>division</r>
  </p>
  <p style="TRH7">
    <r>section3</r>
  </p>
  <p/>
  <p style="TRNormal">
    <r>entitled</r>
  </p>
  <p style="TRNormal">
    <r>demo</r>
  </p>
<p style="TRH7">
    <r>sections4</r>
  </p>
  <p style="TRNormal">
    <r>val demo</r>
  </p>
  <p style="TRNormal">
    <r>val which</r>
  </p>
</text>

我想进行XSLT转换,并将上面的输入转换为下面的预期输出

<?xml version="1.0" encoding="UTF-8"?><chapter><title>Partial Interests</title><division><title>5.01Concurrent Interests</title>
<subdivision><title>[1]In General</title>
<arabicSubdivision><title>Arabic</title>
<section><title>section</title>
<paragraph>An owner.</paragraph><paragraph>On other</paragraph></section>
<section><title>sections</title>
<paragraph>entitled</paragraph><paragraph>in which</paragraph></section></arabicSubdivision></subdivision></division>
<division><title>division</title>
<section><title>section3</title>
<paragraph>entitled</paragraph><paragraph>demo</paragraph>
</section>
</division>
<section><title>sections4</title>
<paragraph>val demo</paragraph>
<paragraph>val which></paragraph>
</section>
</chapter>

借助映射应如何进行分组

有人可以帮助我使用XSLT获得预期的输出吗?因为我是XSLT的新手,做了一些工作但没有获得预期的输出。

我开发的XSLT

<?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"
                exclude-result-prefixes="xs"
                version="2.0">
    <xsl:strip-space elements="*"/>
    <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
    <xsl:template match="node()|@*" mode="pretrans">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="p[count(child::node())=0]" mode="pretrans"/>
    <xsl:template match="doc">
        <chapter-poc>
            <xsl:apply-templates/>
        </chapter-poc>
    </xsl:template>
    <xsl:template match="text">
        <chapter>
        <xsl:variable name="thr5pos" select="count(child::node()[@style='TRH7']/preceding-sibling::p)+1"/>
        <xsl:apply-templates select="child::node()[position()&lt;$thr5pos]" mode="presec"/>
        <section>
            <xsl:variable name="nodesets" >
                <xsl:apply-templates select="child::node()[position()>=$thr5pos]" mode="pretrans"/>
            </xsl:variable>
            <xsl:apply-templates select="$nodesets" mode="postsec"/> <!---->
        </section>
        </chapter>
    </xsl:template>
    <xsl:template match="p" mode="presec">
        <xsl:choose>
            <xsl:when test="@style='TRH2'">
                <title><xsl:apply-templates/></title>
            </xsl:when>
            <xsl:when test="@style='TRH4'">
                <division>
                    <title><xsl:apply-templates/></title>
                </division>
                <xsl:choose>
                <xsl:when test = "@style='TRH5'">
                <subdivision>
                    <title><xsl:apply-templates/></title>
                </subdivision>
                </xsl:when>
                <xsl:when test ="@style='TRH7'">
                <section>
                    <title><xsl:apply-templates/></title>
                </section>
                </xsl:when>
                    <xsl:when test ="@style='TRH6'">
                        <arabicSubdivision>
                        <title><xsl:apply-templates/></title>
                        </arabicSubdivision>
                    </xsl:when>
                    <xsl:when test="@style='TRH7'">
                    <section>
                    <title><xsl:apply-templates/></title>
                </section>
                </xsl:when>
                </xsl:choose>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="p" mode="postsec">
        <xsl:variable name="thr5pos" select="count(preceding-sibling::p[@style='TRH7'][1]/preceding-sibling::p)+1"/>
        <xsl:variable name="pos" select="count(preceding-sibling::p)+1"/>
        <xsl:choose>
            <xsl:when test="@style='TRH7'">
                <title><xsl:apply-templates/></title>
            </xsl:when>
            <xsl:when test="count(child::node())=0"/>
            <xsl:otherwise>
                <paragraph>
                    <xsl:apply-templates/>
                </paragraph>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="r">
            <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="@id">
            <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

我从XSLT获得的输出是

<chapter>
   <title>Partial Interests</title>
   <division>
      <title>5.01Concurrent Interests</title>
   </division>
   <division>
      <title>division</title>
   </division>
   <section>
      <title>sections4</title>
      <paragraph>val demo</paragraph>
      <paragraph>val which</paragraph>
   </section>
</chapter>

与预期输出不同。

推荐答案

以下是对for-each-group group-starting-with使用递归分组的初始尝试(使用XSLT3):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  exclude-result-prefixes="#all"
  xmlns:mf="http://example.com/mf"
  expand-text="yes">
  
  <xsl:param name="levels" as="map(xs:string, map(xs:string, xs:string))*"
    select="map { 'TRH2' : map { 'container' : 'chapter', 'content' : 'title' } }, 
            map { 'TRH4' : map { 'container' : 'division', 'content' : 'title' } },
            map { 'TRH5' : map { 'container' : 'subdivision', 'content' : 'title' } },
            map { 'TRH6' : map { 'container' : 'arabicSubdivision', 'content' : 'title' } },
            map { 'TRH7' : map { 'container' : 'section', 'content' : 'title' } }"/>
  
  <xsl:function name="mf:group" as="node()*">
    <xsl:param name="elements" as="element(p)*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$elements" group-starting-with="p[@style = $levels[$level] => map:keys()]">
      <xsl:choose>
        <xsl:when test="self::p[@style = $levels[$level] => map:keys()]">
          <xsl:element name="{$levels[$level]?*?container}">
            <xsl:element name="{$levels[$level]?*?content}">
              <xsl:value-of select="r"/>
            </xsl:element>
            <xsl:choose>
              <xsl:when test="exists($levels[$level + 1])">
                <xsl:sequence select="mf:group(tail(current-group()), $level + 1)"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="tail(current-group())"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:element>
        </xsl:when>
        <xsl:otherwise>
          <xsl:choose>
            <xsl:when test="exists($levels[$level + 1])">
                <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:function>
  
  <xsl:template match="text">
    <xsl:sequence select="mf:group(*, 1)"/>
  </xsl:template>
  
  <xsl:template match="p[@style = 'TRNormal']">
    <paragraphp>
      <xsl:apply-templates select="r/node()"/>
    </paragraphp>
  </xsl:template>
  
  <xsl:template match="p[not(has-children())]"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

</xsl:stylesheet>

XSLT 3需要Saxon 9.8 HE或更高版本或Saxon-JS 2或Altova XML 2017 R3及更高版本。

但是,应该可以将levels参数设置为某种XML数据结构,而不是轻量级的XPath 3.1映射,即

  <xsl:param name="create-xml-levels">
    <levels>
      <xsl:iterate select="$levels">
        <level key="{map:keys(.)}">
          <container>{?*?container}</container>
          <content>{?*?content}</content>
        </level>
      </xsl:iterate>
    </levels>
  </xsl:param>

会这样做并创建类似

的内容
  <levels>
      <level key="TRH2">
         <container>chapter</container>
         <content>title</content>
      </level>
      <level key="TRH4">
         <container>division</container>
         <content>title</content>
      </level>
      <level key="TRH5">
         <container>subdivision</container>
         <content>title</content>
      </level>
      <level key="TRH6">
         <container>arabicSubdivision</container>
         <content>title</content>
      </level>
      <level key="TRH7">
         <container>section</container>
         <content>title</content>
      </level>
   </levels>

您可以将其存储在使用doc加载的辅助输入中,也可以存储为参数(如下所示):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map"
  exclude-result-prefixes="#all"
  xmlns:mf="http://example.com/mf">
  
  <xsl:param name="xml-levels-param">
  <levels>
      <level key="TRH2">
         <container>chapter</container>
         <content>title</content>
      </level>
      <level key="TRH4">
         <container>division</container>
         <content>title</content>
      </level>
      <level key="TRH5">
         <container>subdivision</container>
         <content>title</content>
      </level>
      <level key="TRH6">
         <container>arabicSubdivision</container>
         <content>title</content>
      </level>
      <level key="TRH7">
         <container>section</container>
         <content>title</content>
      </level>
   </levels>
  </xsl:param>
  
  <xsl:variable name="xml-levels" select="$xml-levels-param/levels/level"/>
  
  <xsl:function name="mf:group-xml" as="node()*">
    <xsl:param name="elements" as="element(p)*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$elements" group-starting-with="p[@style = $xml-levels[$level]/@key]">
      <xsl:choose>
        <xsl:when test="self::p[@style = $xml-levels[$level]/@key]">
          <xsl:element name="{$xml-levels[$level]/container}">
            <xsl:element name="{$xml-levels[$level]/content}">
              <xsl:value-of select="r"/>
            </xsl:element>
            <xsl:choose>
              <xsl:when test="exists($xml-levels[$level + 1])">
                <xsl:sequence select="mf:group-xml(current-group()[position() gt 1], $level + 1)"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="current-group()[position() gt 1]"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:element>
        </xsl:when>
        <xsl:otherwise>
          <xsl:choose>
            <xsl:when test="exists($xml-levels[$level + 1])">
                <xsl:sequence select="mf:group-xml(current-group(), $level + 1)"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:function>
  
  <xsl:template match="text">
    <xsl:sequence select="mf:group-xml(*, 1)"/>
  </xsl:template>
  
  <xsl:template match="p[@style = 'TRNormal']">
    <paragraphp>
      <xsl:apply-templates select="r/node()"/>
    </paragraphp>
  </xsl:template>
  
  <xsl:template match="p[not(node())]"/>

  <xsl:output method="xml" indent="yes"/>

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

我只测试了在Saxon 10中仅使用XSLT 2功能尝试,因此您可以自己测试它是否可以与XSLT 2处理程序一起使用,它应该报告任何不存在的特性或函数的使用,但是使用XPath 2或XSLT 2替代应该很容易。

这篇关于无法使用分组执行XSLT转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆