繁体   English   中英

从两个xml文件中对XSLT进行排序

[英]Sorting in XSLT from two xml files

我有这两个xmls 1> default.xml

<UI-defination>
   <class >
      <list_view >
         <members>
            <member col_span="1" name="code" displayName="Code"/>
            <member col_span="1" name="creationTS" displayName="CreationTS"/>
            <member col_span="1" name="creator" displayName="Creator"/>
            <member col_span="1" name="displayName" displayName="DisplayName"/>
            <member col_span="1" name="emailAddress" displayName="EmailAddress"/>
            <member col_span="1" name="id" displayName="Id"/>
         </members>
      </list_view>
   </class>
</UI-defination>

2> Rules.xml中

<UI-defination>
    <class name="Role">
        <list_view multiselect="true">
            <members>
                <member name="displayName" sequence="3"/>
                <member name="code" sequence="4"/>
            </members>
            <exclude>
                <members>
                    <member name="id"/>
                    <member name="creator"/>
                </members>
            </exclude>
       </list_view>
    </class>
</UI-defination>

我想按照以下规则显示会员元素的列表

  • rules.xml / members / member应从列表中排除
  • 应该根据rules.xml中的sequence属性显示元素
  • 那些不存在于rules.xml / members / member中的元素应该根据default.xml中的自然顺序显示。
    预期产量是......

    creationTS
    电子邮件地址
    显示名称

然后是具有序列attr的元素,它们应该在那个确切的位置。 然后void位置应该被剩余元素的自然顺序填充。(即,没有序列attr。的元素,来自default.xml)

恕我直言,这不是排序问题,而是排队问题。
让我们通过拥有两种类型的成员来简化示例:放置成员,其在队列中的位置是保留的,以及需要填充放置成员之间的间隙的普通成员。

<members>
    <member id="1" place="9"/>
    <member id="2" place="2"/>
    <member id="3" place="5"/>
    <member id="4"/>
    <member id="5"/>
    <member id="6"/>
    <member id="7"/>
    <member id="8"/>
    <member id="9"/>
    <member id="10"/>
    <member id="11"/>
    <member id="12"/>
</members>

排队算法可以描述如下:

  1. 保留的成员按其保留的位置排序。

  2. 每个预留成员“呼叫”一组普通成员坐在它前面。

    在我们的示例中,我们在第2,5和9位预留了成员。在将预留成员放置在第9位之前,我们需要召集3个普通成员组成的小组坐在第6,7和8位。

    该组的大小等于当前成员的保留位置与其先前保留的兄弟位置之间的间隙的大小。 在我们的例子中,这是9 - 5 - 1 = 3。

    这个小组的起始位置可以推断如下:由于前面成员的预留位置是#5,所以已经有5个成员坐下; 其中2名成员(位置#2和#5)是预留成员,因此只有3名普通成员已经就座,我们的小组从5 - 3 + 1 = 4开始。

  3. 最后一个保留成员将其余的普通成员调用到其后。

这是XSLT 1.0中的实现示例:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:variable name="reserved-members">
    <xsl:for-each select="/members/member[@place]">
        <xsl:sort select="@place" data-type="number" order="ascending"/>
        <xsl:copy-of select="."/>
    </xsl:for-each>
</xsl:variable>

<xsl:variable name="ordinary-members" select="/members/member[not(@place)]"/>

<xsl:template match="/">
    <output>
        <xsl:for-each select="exsl:node-set($reserved-members)/member">

            <xsl:variable name="previous-place"> 
                <xsl:choose>
                    <xsl:when test="position()>1">
                        <xsl:value-of select="preceding-sibling::member[1]/@place"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="0"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:variable>

            <xsl:variable name="gap-size" select="@place - $previous-place - 1"/>
            <xsl:variable name="gap-start" select="$previous-place - count(preceding-sibling::member) + 1"/> 

            <!-- fill the gap with ordinary members -->
            <xsl:for-each select="$ordinary-members[$gap-start &lt;= position() and position() &lt; $gap-start + $gap-size]">
                <xsl:copy-of select="."/>
            </xsl:for-each>

            <!-- output the reserved member -->
            <member id="{@id}" place="{@place}" start="{$gap-start}" size="{$gap-size}"/>

            <!-- output remaining ordinary members -->
            <xsl:if test="position()=last()">
                <xsl:for-each select="$ordinary-members[position() >= $gap-start + $gap-size]">
                    <xsl:copy-of select="."/>
                </xsl:for-each>
            </xsl:if>

        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>

当应用于上面的输入示例时,结果是:

<?xml version="1.0" encoding="UTF-8"?>
<output>
   <member id="4"/>
   <member id="2" place="2" start="1" size="1"/>
   <member id="5"/>
   <member id="6"/>
   <member id="3" place="5" start="2" size="2"/>
   <member id="7"/>
   <member id="8"/>
   <member id="9"/>
   <member id="1" place="9" start="4" size="3"/>
   <member id="10"/>
   <member id="11"/>
   <member id="12"/>
</output>

以下假设您只想输出member元素,它使用递归函数从原始元素构造正确的序列,使用在rules.xml中定义位置的元素或使用下一个剩余的元素填充位置元件:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs mf"
  xmlns:mf="http://example.com/mf">

<xsl:param name="rules-uri" select="'Rules.xml'"/>
<xsl:variable name="rules-doc" select="doc($rules-uri)"/>

<xsl:variable name="main-input" select="/"/>

<xsl:output indent="yes"/>

<xsl:key name="member" match="list_view/members/member" use="@name"/>

<xsl:key name="pos" match="list_view/members/member" use="xs:integer(@sequence)"/>

<xsl:variable name="exclude" select="key('member', $rules-doc//exclude/members/member/@name)"/>

<xsl:variable name="process" select="//list_view/members/member except $exclude"/>

<xsl:variable name="no-key" select="$process[not(key('member', @name, $rules-doc))]"/>

<xsl:function name="mf:fill" as="element(member)*">
  <xsl:param name="pos" as="xs:integer"/>
  <xsl:param name="length" as="xs:integer"/>
  <xsl:param name="no-key" as="element(member)*"/>
  <xsl:sequence select="if ($pos gt $length)
                        then ()
                        else 
                          (if (key('pos', $pos, $rules-doc))
                              then (key('member', key('pos', $pos, $rules-doc)/@name, $main-input), 
                                    mf:fill($pos + 1, $length, $no-key))
                              else ($no-key[1], mf:fill($pos + 1, $length, $no-key[position() gt 1])))"/>
</xsl:function>

<xsl:template match="/">
  <xsl:sequence select="mf:fill(1, count($process), $no-key)"/>
</xsl:template>

</xsl:stylesheet>

随着输入

<UI-defination>
   <class >
      <list_view >
         <members>
            <member col_span="1" name="code" displayName="Code"/>
            <member col_span="1" name="creationTS" displayName="CreationTS"/>
            <member col_span="1" name="creator" displayName="Creator"/>
            <member col_span="1" name="displayName" displayName="DisplayName"/>
            <member col_span="1" name="emailAddress" displayName="EmailAddress"/>
            <member col_span="1" name="id" displayName="Id"/>
         </members>
      </list_view>
   </class>
</UI-defination>

和规则定义

<UI-defination>
    <class name="Role">
        <list_view multiselect="true">
            <members>
                <member name="displayName" sequence="2"/>
                <member name="code" sequence="4"/>
            </members>
            <exclude>
                <members>
                    <member name="id"/>
                    <member name="creator"/>
                </members>
            </exclude>
        </list_view>
    </class>
</UI-defination>

我得到了结果

<member col_span="1" name="creationTS" displayName="CreationTS"/>
<member col_span="1" name="displayName" displayName="DisplayName"/>
<member col_span="1" name="emailAddress" displayName="EmailAddress"/>
<member col_span="1" name="code" displayName="Code"/>

以下是我发布的XSLT采用的迈克尔发布的XML输入:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs mf"
  xmlns:mf="http://example.com/mf">

<!--
<xsl:param name="rules-uri" select="'test2014062704.xml'"/>
<xsl:variable name="rules-doc" select="doc($rules-uri)"/>
-->

<xsl:variable name="main-input" select="/"/>

<xsl:output indent="yes"/>

<xsl:key name="member" match="members/member" use="@id"/>

<xsl:key name="pos" match="members/member" use="xs:integer(@place)"/>

<xsl:variable name="process" select="members/member"/>

<xsl:variable name="no-key" select="members/member[not(@place)]"/>

<xsl:function name="mf:fill" as="element(member)*">
  <xsl:param name="pos" as="xs:integer"/>
  <xsl:param name="length" as="xs:integer"/>
  <xsl:param name="no-key" as="element(member)*"/>
  <xsl:sequence select="if ($pos gt $length)
                        then ()
                        else 
                          (if (key('pos', $pos, $main-input))
                              then (key('pos', $pos, $main-input), 
                                    mf:fill($pos + 1, $length, $no-key))
                              else ($no-key[1], mf:fill($pos + 1, $length, $no-key[position() gt 1])))"/>
</xsl:function>

<xsl:template match="/">
  <xsl:sequence select="mf:fill(1, count($process), $no-key)"/>
</xsl:template>

</xsl:stylesheet>

然后用Saxon 9.5输入

<members>
    <member id="1" place="9"/>
    <member id="2" place="2"/>
    <member id="3" place="5"/>
    <member id="4"/>
    <member id="5"/>
    <member id="6"/>
    <member id="7"/>
    <member id="8"/>
    <member id="9"/>
    <member id="10"/>
    <member id="11"/>
    <member id="12"/>
</members>

我得到了结果

<member id="4"/>
<member id="2" place="2"/>
<member id="5"/>
<member id="6"/>
<member id="3" place="5"/>
<member id="7"/>
<member id="8"/>
<member id="9"/>
<member id="1" place="9"/>
<member id="10"/>
<member id="11"/>
<member id="12"/>

这与Michael的XSLT 1.0代码生成的顺序相同。

与@Martin Honnen的回答一样,我的回答也适用于XSLT 1.0(无论它的价值如何):

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

  <xsl:variable name="excludes" select="document('Rules.xml')//exclude/members"/>
  <xsl:variable name="sequences" select="document('Rules.xml')//list_view/members"/>

  <xsl:template match="/">

    <members>
      <!-- output all member with an explicit sequence -->
      <xsl:for-each select="//member">
        <xsl:sort select="$sequences/member[@name=current()/@name]/@sequence"/>

        <xsl:if test="$sequences/member[@name=current()/@name]/@sequence and not($excludes/member[@name=current()/@name])">
          <member><xsl:value-of select="@name"/></member>
        </xsl:if>        
      </xsl:for-each>

      <!-- output all member without an explicit sequence -->
      <xsl:for-each select="//member">

        <xsl:if test="not($sequences/member[@name=current()/@name]/@sequence) and not($excludes/member[@name=current()/@name])">
          <member><xsl:value-of select="@name"/></member>
        </xsl:if>

      </xsl:for-each>

    </members>

  </xsl:template>

</xsl:stylesheet>

产生结果

<?xml version="1.0" encoding="UTF-8"?>
<members>
  <member>displayName</member>
  <member>code</member>
  <member>creationTS</member>
  <member>emailAddress</member>
</members>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM