6

次のような中間変数を (入力データから) 作成する XSLT があります (ハードコーディングされた例ですが、本質的に動的です)。

<xsl:variable name="variableX">
    <ValidCode CodePart="CP1" Code="C1"/>
    <ValidCode CodePart="CP2" Code="C2"/>
    <ValidCode CodePart="CP1" Code="C3"/>
    <ValidCode CodePart="CP2" Code="C4"/>
    <ValidCode CodePart="CP2" Code="C5"/>
</xsl:variable>

CodePart 値の個別の発生をループしたいと考えています。XSLT 2.0 では簡単です。

<xsl:for-each select="distinct-values($variableX/ValidCode/@CodePart)">...</xsl:for-each>

しかし、XSLT 1.0 でこれを行うにはどうすればよいでしょうか?
キーは動的に決定される変数であり、入力ファイルの一部ではないため、使用できないことに注意してください。

私の入力ファイルには、次のように可能なすべてのコード部分のリストが含まれています。

<root>
    <CodePart><value>CP1</value></CodePart>
    <CodePart><value>CP2</value></CodePart>
    <CodePart><value>CP3</value></CodePart>
</root>

そのため、代わりにループオーバーし//CodePart/valueて、初心者向けの一意性を確保することを考えました。しかし、条件を含むXpath式が必要です

「値は、すべての $variableX/ValidCode/@CodePart 値のノード セットで発生します」

そして次のようなものを使用します

<xsl:for-each select="//CodePart[..condition..]/value">...</xsl:for-each>

私が探している Xpath 式の単純な形式はありますか?
または、別のアプローチが望ましいですか?

4

5 に答える 5

4

キーは動的に決定される変数であり、入力ファイルの一部ではないため、使用できないことに注意してください。

これは単に真実ではありません。

これは、キーを使用した短くてシンプルで最も効率的なソリューションです(ところで、条件付き命令を使用していないか、まったく使用していませんxsl:for-each:)):

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

 <xsl:key name="kCodeByPart" match="ValidCode" use="@CodePart"/>

    <xsl:variable name="variableX">
        <ValidCode CodePart="CP1" Code="C1"/>
        <ValidCode CodePart="CP2" Code="C2"/>
        <ValidCode CodePart="CP1" Code="C3"/>
        <ValidCode CodePart="CP2" Code="C4"/>
        <ValidCode CodePart="CP2" Code="C5"/>
    </xsl:variable>

    <xsl:variable name="vCodes" select="ext:node-set($variableX)/*"/>


 <xsl:template match="/">
  <xsl:apply-templates select=
  "$vCodes[generate-id()
          =
           generate-id(key('kCodeByPart', @CodePart)[1])
           ]"/>
 </xsl:template>

 <xsl:template match="ValidCode">

  Code Part: <xsl:value-of select="@CodePart"/>
    Codes: <xsl:apply-templates select=
      "key('kCodeByPart', @CodePart)/@Code"/>
 </xsl:template>

 <xsl:template match="@Code">
   <xsl:value-of select="concat(., ' ')"/>
 </xsl:template>
</xsl:stylesheet>

この変換が任意の XML ドキュメント (使用されていない) に適用されると、必要な正しい結果が生成されます。

  Code Part: CP1
    Codes: C1 C3 

  Code Part: CP2
    Codes: C2 C4 C5 

説明:

W3C XSLT 1.0 勧告で指定されているとおり:

"The key can be used to retrieve a key from a document other than the document containing the context node."

提供されたソリューションでは、この可能性さえ使用しません。コンテキスト ノードと取得されたキーの両方が同じドキュメントからのもの$vCodesです。

ここでも、1 つのキーを複数のドキュメントに使用できることが指定されています。

「スタイルシートは、xsl:key 要素を使用して各ドキュメントの一連のキーを宣言します。」

簡単に言えば、インデックス作成は現在のドキュメント(コンテキスト (現在の) ノードを含むドキュメント) に対して実行されます。

于 2012-06-20T02:57:05.707 に答える
3

distinct-valuesXPath 1.0 では、同じ値を持つ前の兄弟を探すことで関数をエミュレートできます。

最初のドキュメントをこれに単純化したので、簡単に再現できます (XSLT を使用しなくても):

<?xml version="1.0"?>
<root>
    <ValidCode CodePart="CP1" Code="C1"/>
    <ValidCode CodePart="CP2" Code="C2"/>
    <ValidCode CodePart="CP1" Code="C3"/>
    <ValidCode CodePart="CP2" Code="C4"/>
    <ValidCode CodePart="CP2" Code="C5"/>
    <ValidCode CodePart="CP3" Code="C5"/>
</root>

次に、次の XPath 式はCodePart、ドキュメント内で発生する各値を持つ 1 つの属性のみを選択します。

//ValidCode/@CodePart[not(../preceding-sibling::ValidCode/@CodePart = .)]

したがって、属性が選択される条件は、現在検査されている (選択されている) 属性と同じ値を持つ属性を持つ要素CodePartの前の兄弟がないことです。<ValidCode>CodePartCodePart

于 2012-06-19T18:35:15.277 に答える
2

XPath を直接使用できるとは思いませんが、次のように有効な値のみを確認できるはずです。

<xsl:for-each select="//CodePart/value">
  <xsl:variable name="CodePart" select="."/>
  <xsl:if test="$variableX/ValidCode[@CodePart=$CodePart]">
    . . .
  </xsl:if>
</xsl:for-each> 

またはもっと簡単に(コメントのおかげで):

<xsl:for-each select="//CodePart/value">
  <xsl:if test="$variableX/ValidCode[@CodePart=current()]">
    . . .
  </xsl:if>
</xsl:for-each> 

$variableXがノード セットではなく XML フラグメントである場合は、ノード セットに変換する必要があります。これは実装に依存し、Microsoft プロセッサを使用します。

<xsl:for-each select="//CodePart/value" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
  <xsl:if test="msxsl:node-set($variableX)/ValidCode[@CodePart=current()]">
    . . .
  </xsl:if>
</xsl:for-each> 
于 2012-06-19T18:11:29.763 に答える
2

これを(キーなしで)解決する1つの方法は、並べ替えてテストすることですpreceding-sibling

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">

    <xsl:variable name="variableX">
        <ValidCode CodePart="CP1" Code="C1"/>
        <ValidCode CodePart="CP2" Code="C2"/>
        <ValidCode CodePart="CP1" Code="C3"/>
        <ValidCode CodePart="CP2" Code="C4"/>
        <ValidCode CodePart="CP2" Code="C5"/>
     </xsl:variable>

     <xsl:variable name="refvarX" select="msxsl:node-set($variableX)" />

     <xsl:template match="/">
         <root>
             <xsl:for-each select="$refvarX/ValidCode">
                 <xsl:sort select="./@CodePart" data-type="text" />
                 <xsl:if test="not(./@CodePart = preceding-sibling::ValidCode/@CodePart)">
                     <cp>
                         <xsl:value-of select="./@CodePart" />
                     </cp>
                 </xsl:if>
             </xsl:for-each>
         </root>
    </xsl:template>

</xsl:stylesheet>
于 2012-06-19T18:34:05.180 に答える
0

関数の使用node-set()、たとえば saxon の場合:

<xsl:template match="/">
  <xsl:apply-templates select="root/CodePart/value[
   . = saxon:node-set($variableX)/ValidCode/@CodePart]/>
</xsl:template>

<xsl:template match="value">
  <!-- template for distinct value -->
</xsl:template>
于 2012-06-19T21:34:36.017 に答える