我很难制定问题标题。也许这个例子会更有意义。
假设我有一个类似于系统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
的最大节点数,因此在我的示例文档中,我知道单个<address>
节点的最大数量@type
1}}是三个(三个<email_address>
个节点,@type=other
=三个<address>
个节点@name=otherX
}。
答案 0 :(得分:1)
此样式表:
<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>
说明:
答案 1 :(得分:1)
此转换非常简单(只有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()
的串联。
在变量中捕获了两个节点集:一个包含具有phone_number
属性特定值的所有type
元素,另一个包含所有{{1}具有email_address
属性特定值的元素。
对于这两个节点集中较长时间的每个元素,使用一个元素或(如果可能的话,来自两个节点集的一对元素)(在最终输出中省略type
属性`。)