2017-09-22 19 views
0

以下のXMLをフィルタリングするためにXPath 1.0のヘルプが必要です。これは、個別の 'id'と最高の '有効性/ date ':他の子ノードと一致する子ノードと最高値を持つノードを取得するXPath

<foo name="fooName"> 
    <bar name="barName"> 
     <id>1111</id> 
     <validity> 
      <date>20170920</date> 
     </validity> 
    </bar> 
    <bar name="barName"> 
     <id>1111</id> 
     <validity> 
      <date>20170922</date> 
     </validity> 
    </bar> 
    <bar name="barName"> 
     <id>1111</id> 
     <validity> 
      <date>20170921</date> 
     </validity> 
    </bar> 
    <bar name="barName"> 
     <id>2222</id> 
     <validity> 
      <date>20170921</date> 
     </validity> 
    </bar> 
    <bar name="barName"> 
     <id>2222</id> 
     <validity> 
      <date>20170923</date> 
     </validity> 
    </bar> 
</foo> 

私は多くのオプションとリサーチを試みましたが、正確な解決策を見つけられませんでした。あなたはmichael.hor257kはすでにあなたにポインタを与えているために「Muenchianグループ化」、上に読んでください

<foo name="fooName"> 
    <bar name="barName"> 
     <id>1111</id> 
     <validity> 
      <date>20170922</date> 
     </validity> 
    </bar> 
    <bar name="barName"> 
     <id>2222</id> 
     <validity> 
      <date>20170923</date> 
     </validity> 
    </bar> 
</foo> 
+2

これは基本的にグループ化の問題です。http://www.jenitennison.com/xslt/grouping/muenchian.htmlを参照してください。各グループ内では、日付でソートして最初の(または最後の)レコードを出力します。 –

+0

@ michael.hor257kありがとうございます。私はMuenchianのグループ分けについてもっと探求します。 – esb2010

答えて

0

を示唆したように、私は以下のXSLTを思い付きましたこれはうまくいくようです:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
    <xsl:key name="bars-by-id" match="foo/bar" use="id" /> 
    <xsl:template match="foo"> 
     <foo name="fooName"> 
      <xsl:for-each select="bar[count(. | key('bars-by-id', id)[1]) = 1]"> 
       <xsl:variable name="currentID" select="id" /> 
       <xsl:variable name="barsForID" select="key('bars-by-id', $currentID)"/> 
       <xsl:copy-of select="$barsForID[not(../bar[id=$currentID]/validity/date > validity/date)]" /> 
      </xsl:for-each> 
     </foo> 
    </xsl:template> 
</xsl:stylesheet> 

おかげで本当に助けになりました。私を修正してください。

1

は、フィルタリング後のXMLは次のようになります予想されます。 (Web検索では他にもたくさんの検索があります)

Muenchianのグループ分けは、原則としてそれがなくてもできることをより早く行います。状況によっては、追加されたスピードが「原理的に可能」と「実際に実行可能」の違いになります。しかし、状況によっては、この問題に対する単純なアプローチで十分です。

問題1: 'ID'ごとに1つの 'bar'要素が出力に必要です。 ID 1111または2222の棒のどれもが入力内に一意のIDを持たないので、あなたの説明出力が間違っていることを示しています。

この問題を解決する1つの方法: 'bar'の2つのテンプレートを作成します.1つは 'id'が最初に出現したときに発生します(実際は 'id'の別の値です最大の有効性/日付の価値を見出す作業)、そしてその「id」で後に出現するすべての「bar」を無視させるものが無視されます。

<xsl:template match="bar" priority="10.0"> 
    <!--* find the highest validity/date with this ID here, 
     * do what needs to be done. *--> 
    ... 
</xsl:template> 
<xsl:template match="bar[id = preceding-sibling::bar/id]" 
       priority="20.0"/> 

私は将来-私を、私はここに巧妙なものにしようとしていることを警告する明示的な優先順位を与えてくれた(と変更するように試合のパターンを変更することで、それを台無しから未来 - 私を防ぐために相対的な優先順位)。

これを行うもう1つの方法は、 'bar'のテンプレート内に選択/いつ配置するかです。

<xsl:template match="bar"> 
    <xsl:variable name="id" select="string(id)"/> 
    <xsl:choose> 
    <xsl:when test="preceding::bar[id=$id]"/> 
    <xsl:otherwise> 
     <!--* this is the first of this ID, deal with this ID now *--> 
     ... 
    </ 
    </ 
</ 

この2番目のパターンは、実際に出力にコピーしたい 'bar'要素を見つけるために必要なロジックを定式化しやすくすることがあります。あなたは最高の有効/日付値と各IDのない最初のインスタンスを処理したいのですが、インスタンス(複数可):

<xsl:template match="bar"> 
    <xsl:variable name="id" select="string(id)"/> 

    <xsl:choose> 
    <!--* the behavior of comparisons here requires a little 
     * bit of standing on our heads. We want this 'bar' if 
     * its validity/date value is greater than or equal to 
     * all other such values for this ID. So first we filter 
     * out all cases where there is a higher validity/date value 
     * on another 'bar' with this ID. *--> 
    <xsl:when test="validity/date &lt; //bar[id=$id]/validity/date"/> 

    <!--* The 'otherwise' case handles situations where this 
     * is the only 'bar' with this ID, or where there is no 
     * higher validity/date value. *--> 
    <xsl:otherwise> 
     <xsl:copy-of select="."/> 
    </ 
    </ 
</ 

これは「管理可能」に関する一回限りまたは実行していない、ほとんどのスタイルシートに実行された場合あなたが既に鍵とその使い方をよく理解していない限り、このパターンはMuenchianのグループ分けよりも理解しやすいかもしれません。それが遅すぎる場合、Muenchianグループは通常、同じことを達成するより速い方法であることをあなたに示します。

[注:答えの最初のバージョンはmaxdate変数

<xsl:variable name="maxdate" 
       select="max(//bar[id=$id]/validity/date)"/> 

を持っていただけで、それに現在の値を比較した:

<xsl:when test="validity/date = $maxdate"> 
    <xsl:copy-of select="."/> 
</ 

しかし、XPath 1.0の唯一の集約関数は、カウントされています()とsum()です。私は「これがXSLT 2.0でどれくらい簡単にできるか見てみましょうか?もしあなたが2歳になったら。0全体の事はちょうど

<xsl:sequence select="for $v in distinct-values(//bar/id) 
    for $max in max(//bar[id=$v]/validity/date) 
    return //bar[id=$v and validity/date = $max]"/> 

ようなものになるだろうとmax()関数は、本当にそんなに単純なものを作るのは比較的控えめな役割を果たしている。]

+0

XSLT 1.0にmax()関数はありません。 –

+0

かなり良いです。私の最初の答えに誤りがあります。今修正されました(...私が代わりに別のエラーに置き換えていない限り)。 –

+0

私はXSLT 2.0がどれほどシンプルかを見てから使いたいと思っていますが、これは私のコントロール下にないものです。素敵な説明をありがとう。私のXMLは30〜50 MBの範囲に入る可能性があるので、Muenchianのグループ分けを進めました。 – esb2010

関連する問題