3

質問

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に聞こえませんが、(確かに?)正しいアプローチではない可能性があります。

質問は長いです、私は実際の場合と比較してそれを単純化しようとしました。

4

1 に答える 1

1

これはそれを行う必要があります:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <xsl:key name="kChange" match="changes" use="@app" />

  <!-- A key for locating <list>s by the combination of their @app and @kind-->
  <xsl:key name="kList" match="changes/list" use="concat(../@app, '+', @kind)" />

  <!-- A node-set of the first instance of each <list> for each distinct
       pair of @app + @kind -->
  <xsl:variable name="distinctLists"
                select="//changes/list[generate-id() = 
                           generate-id(key('kList', 
                                           concat(../@app, '+', @kind) )[1]
                                      )]"/>

  <!-- Identity template -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <mergedHistory>
      <!-- Apply templates on distinct <changes> elements -->
      <xsl:apply-templates select="build/changes[generate-id() = 
                                   generate-id(key('kChange', @app)[1])]" />
    </mergedHistory>
  </xsl:template>

  <!-- Each distinct <changes> (based on @app) will be sent to this template -->
  <xsl:template match="changes">
    <changes>
      <xsl:apply-templates select="@*" />

      <!-- Apply templates on each distinct <list> with the same @app
           as the current context-->
      <xsl:apply-templates select="$distinctLists[../@app = current()/@app]" />
    </changes>
  </xsl:template>

  <!-- Each distinct <list> (based on @app and @kind) will be 
       sent to this template -->
  <xsl:template match="list">
    <list>
      <xsl:apply-templates select="@*" />

      <!-- Apply templates on all <li>s below <list>s with the same @app and @kind
           as the current one -->
      <xsl:apply-templates select="key('kList', concat(../@app, '+', @kind))/li">
        <xsl:sort select="@priority" order="ascending" data-type="number"/>
      </xsl:apply-templates>
    </list>
  </xsl:template>
</xsl:stylesheet>

ここで注意するテクニックは、単一の値ではなく、値のペアに基づいてアイテムにキーを設定し、それを使用して、値のペアに基づいて個別のインスタンスを検索し、次に同じ値のペアを持つすべてのインスタンスを検索することです。 。

これをサンプル入力で実行すると、要求された出力が生成されます。

<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>
于 2013-02-08T20:24:42.830 に答える