很抱歉,我已经更新了以下问题。
虽然之前看起来非常简单,但后来变成了一件非常复杂的事情。 任何人都可以帮助我吗?
我需要这个XML
<?xml version="1.0" encoding="UTF-8"?>
<RecordSet>
<Data image="h1.gif" description="a"/>
<Data image="" description="asdf" />
<Data image="" description="bsdf"/>
<Data image="" description="csdf"/>
<Data image="h2.gif" description="b"/>
<Data image="" description="dsdf"/>
<Data image="" description="esdf"/>
<Data image="h3.gif" description="c"/>
<Data image="" description="sdff"/>
</RecordSet>
转换为此
<RecordSet>
<MenuHeader image="h1.gif">
<Menu description="a"/>
<Menu description="b"/>
<Menu description="c"/>
</MenuHeader>
<MenuHeader image="h2.gif">
<Menu description="d"/>
<Menu description="e"/>
</MenuHeader>
<MenuHeader image="h3.gif">
<Menu description="f"/>
</MenuHeader>
</RecordSet>
使用样式表
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="RecordSet">
<RecordSet>
<xsl:for-each select="Data[not(@image='')]">
<MenuHeader image="{@image}">
<xsl:copy-of select="key('d',@image)"/>
</MenuHeader>
</xsl:for-each>
</RecordSet>
</xsl:template>
<xsl:key name="d"
match="Data[@description]"
use="preceding-sibling::Data[@image][1]/@image"/>
</xsl:stylesheet>
实际输出:
<?xml version="1.0" encoding="UTF-8"?>
<RecordSet>
<MenuHeader image="h1.gif">
<Data image="" description="asdf"/>
</MenuHeader>
<MenuHeader image="h2.gif">
<Data image="" description="dsdf"/>
</MenuHeader>
<MenuHeader image="h3.gif">
<Data image="" description="sdff"/>
</MenuHeader>
</RecordSet>
更新: 具有value属性的image属性的Data节点应设置为MenuHeader。任何帮助都会有很大的帮助。
答案 0 :(得分:1)
这在XSLT 2中更容易,但是过去使用XSLT 1:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="RecordSet">
<RecordSet>
<xsl:for-each select="Data[string(@image)]">
<MenuHeader image="{@image}">
<xsl:for-each select="key('d',@image)">
<Menu>
<xsl:copy-of select="@description"/>
</Menu>
</xsl:for-each>
</MenuHeader>
</xsl:for-each>
</RecordSet>
</xsl:template>
<xsl:key name="d"
match="Data[not(string(@image))][@description]"
use="preceding-sibling::Data[string(@image)][1]/@image"/>
</xsl:stylesheet>
可生产
<?xml version="1.0" encoding="utf-8"?>
<RecordSet>
<MenuHeader image="h1.gif">
<Menu description="asdf"/>
<Menu description="bsdf"/>
<Menu description="csdf"/>
</MenuHeader>
<MenuHeader image="h2.gif">
<Menu description="dsdf"/>
<Menu description="esdf"/>
</MenuHeader>
<MenuHeader image="h3.gif">
<Menu description="sdff"/>
</MenuHeader>
</RecordSet>
请注意,如果您想要image=""
而非没有图片属性的问题的更新版本,则测试为string(@image)
,而不仅仅是@image
。
答案 1 :(得分:1)
2.0选项,推送风格。
XML输入
<RecordSet>
<Data image="h1.gif"/>
<Data description="a"/>
<Data description="b"/>
<Data description="c"/>
<Data image="h2.gif"/>
<Data description="d"/>
<Data description="e"/>
<Data image="h3.gif"/>
<Data description="f"/>
</RecordSet>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="Data" group-starting-with="Data[@image]">
<MenuHeader image="{@image}">
<xsl:apply-templates select="current-group()[not(@image)]"/>
</MenuHeader>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<Menu>
<xsl:apply-templates select="@*|node()"/>
</Menu>
</xsl:template>
</xsl:stylesheet>
<强>输出强>
<RecordSet>
<MenuHeader image="h1.gif">
<Menu description="a"/>
<Menu description="b"/>
<Menu description="c"/>
</MenuHeader>
<MenuHeader image="h2.gif">
<Menu description="d"/>
<Menu description="e"/>
</MenuHeader>
<MenuHeader image="h3.gif">
<Menu description="f"/>
</MenuHeader>
</RecordSet>
答案 2 :(得分:1)
这是使用尾递归来近似“while”循环的另一种方法
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="/RecordSet">
<RecordSet>
<xsl:apply-templates select="Data[@image != '']"/>
</RecordSet>
</xsl:template>
<xsl:template match="Data[@image != '']">
<MenuHeader image="{@image}">
<xsl:apply-templates select="following-sibling::Data[1][@image='']" />
</MenuHeader>
</xsl:template>
<xsl:template match="Data">
<Menu description="{@description}"/>
<xsl:apply-templates select="following-sibling::Data[1][@image='']" />
</xsl:template>
</xsl:stylesheet>
这里的技巧是生成MenuHeader
的模板仅将模板应用于其后的第一个(如果有)非图像Data
元素。该模板然后以递归方式消耗下一个等,直到我们到达下一个具有image
的点,此时递归将自动停止。
答案 3 :(得分:1)
接受的答案不会在您的原始帖子中产生您想要的输出。这个稍微修改过的版本(仍然是XSLT 1.0)应该可以解决问题。
这是一个不同的XSLT 1.0替代方案。
当这个XSLT:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key
name="kDataByPrecedingImage"
match="Data[@image = '']"
use="generate-id(preceding-sibling::*[@image != ''][1])"/>
<xsl:template match="RecordSet">
<RecordSet>
<xsl:apply-templates select="*[@image != '']"/>
</RecordSet>
</xsl:template>
<xsl:template match="Data">
<MenuHeader image="{@image}">
<xsl:apply-templates
select="key('kDataByPrecedingImage', generate-id())"
mode="children"/>
</MenuHeader>
</xsl:template>
<xsl:template match="Data" mode="children">
<Menu description="{@description}"/>
</xsl:template>
</xsl:stylesheet>
...适用于给定的XML:
<?xml version="1.0" encoding="UTF-8"?>
<RecordSet>
<Data image="h1.gif" description="a"/>
<Data image="" description="asdf"/>
<Data image="" description="bsdf"/>
<Data image="" description="csdf"/>
<Data image="h2.gif" description="b"/>
<Data image="" description="dsdf"/>
<Data image="" description="esdf"/>
<Data image="h3.gif" description="c"/>
<Data image="" description="sdff"/>
</RecordSet>
...产生了所需的结果:
<?xml version="1.0" encoding="UTF-8"?>
<RecordSet>
<MenuHeader image="h1.gif">
<Menu description="asdf"/>
<Menu description="bsdf"/>
<Menu description="csdf"/>
</MenuHeader>
<MenuHeader image="h2.gif">
<Menu description="dsdf"/>
<Menu description="esdf"/>
</MenuHeader>
<MenuHeader image="h3.gif">
<Menu description="sdff"/>
</MenuHeader>
</RecordSet>