[英]XSLT 1.0 - Merge sibling nodes with child nodes into new composite nodes
我很難確定問題的標題。 也許這個例子會更有意義。
假設我有一個來自系統A的XML文檔,如下所示:
<root>
<phone_numbers>
<phone_number type="work">123-WORK</phone_number>
<phone_number type="home">456-HOME</phone_number>
<phone_number type="work">789-WORK</phone_number>
<phone_number type="other">012-OTHER</phone_number>
</phone_numbers>
<email_addresses>
<email_address type="home">a@home</email_address>
<email_address type="other">b@other</email_address>
<email_address type="home">c@home</email_address>
<email_address type="work">d@work</email_address>
<email_address type="other">e@other</email_address>
<email_address type="other">f@other</email_address>
</email_addresses>
</root>
而且我必須將它們放入這樣的結構中,以便可以在系統B中使用它們:
<root>
<addresses>
<address name="work1">
<phone_number>123-WORK</phone_number>
<email_address>d@work</email_address>
</address>
<address name="work2">
<phone_number>789-WORK</phone_number>
</address>
<address name="other1">
<phone_number>012-OTHER</phone_number>
<email_address>b@other</email_address>
</address>
<address name="other2">
<email_address>e@other</email_address>
</address>
<address name="other3">
<email_address>f@other</email_address>
</address>
<address name="home1">
<phone_number>456-HOME</phone_number>
<email_address>a@home</email_address>
</address>
<address name="home2">
<email_address>c@home</email_address>
</address>
</addresses>
</root>
每種類型的電子郵件地址可以有任意數量(據我所知,從0到無窮大)。 每種類型也可以有任意數量的電話號碼,並且一種類型的電話號碼不必與相同類型的電子郵件地址的數量匹配。
第一個文檔中的電子郵件地址和電話號碼之間並沒有真正的關聯,只是它們的輸入順序與添加到系統A的順序相同。
我必須按類型將電子郵件和電話號碼配對以適合系統B,並且我想將它們配對,以便將X類型的第一個電話號碼與X類型的第一個電子郵件地址配對,這樣就沒有電話號碼X類型的電子郵件與X類型以外的電子郵件配對。
由於我必須將它們配對,並且由於它們進入系統的順序是找到它們之間關系的最接近的順序,因此我想以此方式對其進行排序。 我必須告訴用戶仔細檢查結果,以確保它們有意義,但我必須將它們配對-別無選擇。
使事情復雜化的是,我的實際XML文檔具有更多需要與phone_numbers和email_addresses合並的節點,並且我有兩個以上的@types
。
另一注:我已經計算了任何給定@type
的最大節@type
,因此在我的示例文檔中,我知道單個@type
的<address>
節點的最大數目為三個(三個<email_address>
節點@type=other
=三個<address>
節點,其中@name=otherX
)。
此樣式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="byType" match="/root/*/*" use="@type" />
<xsl:key name="phoneByType" match="phone_numbers/phone_number"
use="@type" />
<xsl:key name="emailByType" match="email_addresses/email_address"
use="@type" />
<xsl:template match="/">
<root>
<addresses>
<xsl:apply-templates />
</addresses>
</root>
</xsl:template>
<xsl:template match="/root/*/*" />
<xsl:template
match="/root/*/*[generate-id()=generate-id(key('byType', @type)[1])]">
<xsl:apply-templates select="key('phoneByType', @type)"
mode="wrap" />
<xsl:apply-templates
select="key('emailByType', @type)
[position() > count(key('phoneByType', @type))]"
mode="wrap" />
</xsl:template>
<xsl:template match="phone_numbers/phone_number" mode="wrap">
<xsl:variable name="pos" select="position()" />
<address name="{concat(@type, $pos)}">
<xsl:apply-templates select="." mode="out" />
<xsl:apply-templates select="key('emailByType', @type)[$pos]"
mode="out" />
</address>
</xsl:template>
<xsl:template match="email_addresses/email_address" mode="wrap">
<address
name="{concat(@type,
position() + count(key('phoneByType', @type)))}">
<xsl:apply-templates select="." mode="out" />
</address>
</xsl:template>
<xsl:template match="/root/*/*" mode="out">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
在此輸入上:
<root>
<phone_numbers>
<phone_number type="work">123-WORK</phone_number>
<phone_number type="home">456-HOME</phone_number>
<phone_number type="work">789-WORK</phone_number>
<phone_number type="other">012-OTHER</phone_number>
</phone_numbers>
<email_addresses>
<email_address type="home">a@home</email_address>
<email_address type="other">b@other</email_address>
<email_address type="home">c@home</email_address>
<email_address type="work">d@work</email_address>
<email_address type="other">e@other</email_address>
<email_address type="other">f@other</email_address>
<email_address type="test">g@other</email_address>
</email_addresses>
</root>
產生:
<root>
<addresses>
<address name="work1">
<phone_number>123-WORK</phone_number>
<email_address>d@work</email_address>
</address>
<address name="work2">
<phone_number>789-WORK</phone_number>
</address>
<address name="home1">
<phone_number>456-HOME</phone_number>
<email_address>a@home</email_address>
</address>
<address name="home2">
<email_address>c@home</email_address>
</address>
<address name="other1">
<phone_number>012-OTHER</phone_number>
<email_address>b@other</email_address>
</address>
<address name="other2">
<email_address>e@other</email_address>
</address>
<address name="other3">
<email_address>f@other</email_address>
</address>
<address name="test1">
<email_address>g@other</email_address>
</address>
</addresses>
</root>
說明:
這種轉換非常簡單 (只有3個模板,沒有模式):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kTypeByVal" match="@type" use="."/>
<xsl:key name="kPhNumByType" match="phone_number"
use="@type"/>
<xsl:key name="kAddrByType" match="email_address"
use="@type"/>
<xsl:variable name="vallTypes" select=
"/*/*/*/@type
[generate-id()
=
generate-id(key('kTypeByVal',.)[1])
]"/>
<xsl:template match="/">
<root>
<addresses>
<xsl:apply-templates select="$vallTypes"/>
</addresses>
</root>
</xsl:template>
<xsl:template match="@type">
<xsl:variable name="vcurType" select="."/>
<xsl:variable name="vPhoneNums" select="key('kPhNumByType',.)"/>
<xsl:variable name="vAddresses" select="key('kAddrByType',.)"/>
<xsl:variable name="vLonger" select=
"$vPhoneNums[count($vPhoneNums) > count($vAddresses)]
|
$vAddresses[not(count($vPhoneNums) > count($vAddresses))]
"/>
<xsl:for-each select="$vLonger">
<xsl:variable name="vPos" select="position()"/>
<address name="{$vcurType}{$vPos}">
<xsl:apply-templates select="$vPhoneNums[position()=$vPos]"/>
<xsl:apply-templates select="$vAddresses[position()=$vPos]"/>
</address>
</xsl:for-each>
</xsl:template>
<xsl:template match="phone_number|email_address">
<xsl:copy>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
當應用於提供的XML文檔 (以及具有描述屬性的任何文檔)時:
<root>
<phone_numbers>
<phone_number type="work">123-WORK</phone_number>
<phone_number type="home">456-HOME</phone_number>
<phone_number type="work">789-WORK</phone_number>
<phone_number type="other">012-OTHER</phone_number>
</phone_numbers>
<email_addresses>
<email_address type="home">a@home</email_address>
<email_address type="other">b@other</email_address>
<email_address type="home">c@home</email_address>
<email_address type="work">d@work</email_address>
<email_address type="other">e@other</email_address>
<email_address type="other">f@other</email_address>
</email_addresses>
</root>
產生想要的正確結果 :
<root>
<addresses>
<address name="work1">
<phone_number>123-WORK</phone_number>
<email_address>d@work</email_address>
</address>
<address name="work2">
<phone_number>789-WORK</phone_number>
</address>
<address name="home1">
<phone_number>456-HOME</phone_number>
<email_address>a@home</email_address>
</address>
<address name="home2">
<email_address>c@home</email_address>
</address>
<address name="other1">
<phone_number>012-OTHER</phone_number>
<email_address>b@other</email_address>
</address>
<address name="other2">
<email_address>e@other</email_address>
</address>
<address name="other3">
<email_address>f@other</email_address>
</address>
</addresses>
</root>
說明 :
使用Muenchian方法進行分組,將type
屬性的所有不同值收集在$vallTypes
變量中。
對於上面1.中找到的每個不同值, <address>
元素輸出如下。
生成一個name
屬性 ,其值是當前type
和當前position()
的串聯。
在變量中捕獲了兩個節點集 :一個包含所有具有其type
屬性特定值的phone_number
元素,另一個包含所有具有type
屬性特定值的email_address
元素。
對於這兩個節點集中較長者的每個元素,在最終輸出中將使用一個元素或(如果可能,則是兩個節點集中的一對元素)生成 (省略type
屬性`)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.