问题
使用 xsl,如何通过按属性值对元素进行分组来更改元素层次结构,而不假设属性值?
问题描述
文档的上下文如下:<releaseHistory/>
随着新版本的发布(),xml 跟踪软件框架的更改说明( <build/>
)。这个框架有几个应用程序/组件(<changes app='LibraryA|Driver|...'/>
)。更改说明记录了新功能或错误修复 ( <list kind='New|Enhancement'/>
)。
我想转换此文档,以便将不同构建中的所有更改说明合并到按“app”属性值和“种类”属性值分组的列表中,列表项(<li/>
)按“优先级”属性排序。
此外,不应假设“app”和“kind”属性值。请注意,如果需要,如果架构不理想,我可以更改 xml 的架构。
当前状态
- 我能做的:
- 检索唯一“app”和“kind”属性值的列表。
- 一个模板,它以“app”和“kind”为参数,并遍历 xml 文档以合并其属性与参数匹配的所有元素
- 什么不见了:
- 在上面的唯一属性值列表上“循环”并应用模板
输入和预期输出
xml文档:
<?xml version="1.0" encoding="UTF-8"?>
<releaseHistory>
<build>
<description>A killer update</description>
<changes app='LibraryA'>
<list kind='New'>
<li priority='4'>Added feature about X</li>
<li priority='2'>Faster code for big matrices</li>
</list>
<list kind='Enhancement'>
<li priority='1'>Fixed integer addition</li>
</list>
</changes>
<changes app='Driver'>
<list kind='New'>
<li priority='3'>Supporting new CPU models</li>
<li priority='4'>Cross-platform-ness</li>
</list>
</changes>
</build>
<build>
<description>An update for Easter</description>
<changes app='LibraryA'>
<list kind='New'>
<li priority='1'>New feature about Y</li>
</list>
<list kind='Enhancement'>
<li priority='2'>Fixed bug 63451</li>
</list>
</changes>
<changes app='LibraryVector'>
<list kind='Enhancement'>
<li priority='5'>Fixed bug 59382</li>
</list>
</changes>
<changes app='Driver'>
<list kind='New'>
<li priority='0'>Compatibility with hardware Z</li>
</list>
</changes>
</build>
</releaseHistory>
预期文件:
<?xml version="1.0" encoding="UTF-8"?>
<mergedHistory>
<changes app='LibraryA'>
<list kind='New'>
<li priority='1'>New feature about Y</li>
<li priority='2'>Faster code for big matrices</li>
<li priority='4'>Added feature about X</li>
</list>
<list kind='Enhancement'>
<li priority='1'>Fixed integer addition</li>
<li priority='2'>Fixed bug 63451</li>
</list>
</changes>
<changes app='Driver'>
<list kind='New'>
<li priority='0'>Compatibility with hardware Z</li>
<li priority='3'>Supporting new CPU models</li>
<li priority='4'>Cross-platform-ness</li>
</list>
</changes>
<changes app='LibraryVector'>
<list kind='Enhancement'>
<li priority='5'>Fixed bug 59382</li>
</list>
</changes>
</mergedHistory>
部分解决方案
我“已经”能够使用 xsl 列出唯一的“app”和“kind”属性。让我们详细介绍一下 xsl 的当前状态
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"
>
检索所有不同的“应用程序”属性值(LibraryA、Driver、...)<changes app='...'/>
并将它们存储在一个变量中(可能是一个参数):
<xsl:key name="appDistinct" match="changes" use="@app"/>
<xsl:variable name="applicationListVarTmp">
<list>
<xsl:for-each select="//changes[generate-id() = generate-id(key('appDistinct', @app)[1])]">
<li>
<xsl:value-of select="normalize-space(@app)"/>
</li>
</xsl:for-each>
</list>
</xsl:variable>
检索所有不同的“种类”属性值(新,增强)<list kind='...'/>
:
<xsl:key name="kindDistinct" match="changes/list" use="@kind"/>
<xsl:variable name="kindListVar">
<list>
<xsl:for-each select="//changes/list[generate-id() = generate-id(key('kindDistinct', @kind)[1])]">
<li>
<xsl:value-of select="normalize-space(@kind)"/>
</li>
</xsl:for-each>
</list>
</xsl:variable>
<li/>
将所有给定的“应用程序”和“种类”(按优先级排序)与参数合并的模板:
<xsl:template name="mergeSameKindChangesForAnApp">
<xsl:param name="application" />
<xsl:param name="kindness" />
<list><xsl:attribute name='kind'><xsl:value-of select="$kindness"/></xsl:attribute>
<xsl:for-each select="//changes[@app=$application]/list[@kind=$kindness]/li">
<xsl:sort select="@priority" data-type="number" order="ascending"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:copy-of select="./*"/>
</xsl:copy>
</xsl:for-each>
</list>
</xsl:template>
现在,我被困的地方是关于“循环”appListVar
并kindListVar
应用模板。
如果所有 'app' 和 'kind' 都是硬编码的,我可以拨打几个电话,例如:
<xsl:call-template name="mergeSameKindChangesForAnApp">
<changes app='LibraryA'>
<xsl:with-param name="application">
LibraryA
</xsl:with-param>
<xsl:with-param name="kindness">
New
</xsl:with-param>
</changes>
</xsl:call-template>
但我想循环在 xml 文档中找到的 'app's 和 'kind's。exsl:node-set()
例如,我可以这样做
<xsl:param name="applicationListVar" select="exsl:node-set($applicationListVarTmp)" />
<xsl:call-template name="mergeSameKindChangesForAnApp">
<changes app='LibraryA'>
<xsl:with-param name="application">
<xsl:value-of select="$applicationListVar/list/li[2]"/>
</xsl:with-param>
<xsl:with-param name="kindness">
New
</xsl:with-param>
</changes>
</xsl:call-template>
但是,如何循环$applicationListVar/list/li
元素?“循环”听起来不像 xslt-ilish,可能(肯定?)这不是正确的方法。
这个问题很长,与实际情况相比,我试图简化它。