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

时间:2014-06-27 11:39:14

标签: xml xslt xpath xslt-2.0

我有这两个xmls 1 GT; 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
    EMAILADDRESS
    显示名
    代码

3 个答案:

答案 0 :(得分:3)

  

然后是具有序列attr的元素,它们应该在那个   确切的位置。然后空洞位置应该被自然填充   剩余元素的顺序(即由没有元素的元素   序列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. 最后一个保留成员将其余的普通成员召集到其后。

  4. 以下是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>
    

答案 1 :(得分:1)

以下假设您只想输出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代码生成的顺序相同。

答案 2 :(得分:1)

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