[英]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>
我想按照以下規則顯示會員元素的列表
然后是具有序列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>
排隊算法可以描述如下:
保留的成員按其保留的位置排序。
每個預留成員“呼叫”一組普通成員坐在它前面。
在我們的示例中,我們在第2,5和9位預留了成員。在將預留成員放置在第9位之前,我們需要召集3個普通成員組成的小組坐在第6,7和8位。
該組的大小等於當前成員的保留位置與其先前保留的兄弟位置之間的間隙的大小。 在我們的例子中,這是9 - 5 - 1 = 3。
這個小組的起始位置可以推斷如下:由於前面成員的預留位置是#5,所以已經有5個成員坐下; 其中2名成員(位置#2和#5)是預留成員,因此只有3名普通成員已經就座,我們的小組從5 - 3 + 1 = 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 <= position() and position() < $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.