質問
xslを使用して、属性値を想定せずに、属性値で要素をグループ化して要素階層を変更するにはどうすればよいですか?
問題の説明
ドキュメントのコンテキストは次のとおりです。xmlは、<releaseHistory/>
新しいバージョンがリリースされたときにソフトウェアフレームワークの変更メモ()を追跡します(<build/>
)。このフレームワークにはいくつかのアプリ/コンポーネントがあります(<changes app='LibraryA|Driver|...'/>
)。変更メモには、新機能またはバグ修正が記録されます(<list kind='New|Enhancement'/>
)。
<li/>
異なるビルドにまたがるすべての変更メモが、「app」属性値と「kind」属性値でグループ化されたリストにマージされ、リスト項目( )が「priority」属性でソートされるように、このドキュメントを変換したいと思います。
さらに、「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"
>
すべての個別の「app」属性値(LibraryA、Driver、...)を取得し<changes app='...'/>
、それらを変数(paramの場合もあります)に格納します。
<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
適用するための「ループ」についてです。
すべての「アプリ」と「種類」がハードコーディングされている場合、次のようないくつかの呼び出しを行うことができます。
<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ドキュメントにある「アプリ」と「種類」をループしたいと思います。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に聞こえませんが、(確かに?)正しいアプローチではない可能性があります。
質問は長いです、私は実際の場合と比較してそれを単純化しようとしました。