以前の回答 ( XSLT For-Each loop to get data from enumerated tags? ) に続いて、これには簡単な変更が必要です。とはいえ、途中で道に迷ったということで、最後に説明を加えておきます。
この XSLT の場合:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable
name="vNums"
select="'0123456789'"/>
<xsl:key
name="kElemByNumber"
match="Demographic_Information/*"
use="translate(name(), translate(name(), $vNums, ''), '')"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Demographic_Information">
<Demographic_Information>
<xsl:apply-templates
select="*[generate-id() =
generate-id(key(
'kElemByNumber',
translate(name(), translate(name(), $vNums, ''),
''
))[1])][normalize-space()]">
<xsl:sort
select="translate(name(), translate(name(), $vNums, ''), '')"
data-type="number"/>
</xsl:apply-templates>
</Demographic_Information>
</xsl:template>
<xsl:template match="Demographic_Information/*">
<Person subject="{position()}">
<xsl:apply-templates
select="key('kElemByNumber', position())"
mode="children">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="Demographic_Information/*" mode="children">
<xsl:element name="{translate(name(), $vNums, '')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...ソース XML に対して適用されます (「他の/その他の XML データ」のインスタンスがコメントになるように少し変更されています [したがって、正しい XML ドキュメントを保証します]):
<Root>
<Subjects>
<!--<...more XML Data>-->
<Data>
<!--<...other XML Data>-->
<Demographic_Information>
<Age1>33</Age1>
<Age2>66</Age2>
<Age3/>
<Age4/>
<Age5/>
<Age6/>
<Age7/>
<Age8/>
<Age9/>
<Age10/>
<Gender1>M</Gender1>
<Gender2>F</Gender2>
<Gender3/>
<Gender4/>
<Gender5/>
<Gender6/>
<Gender7/>
<Gender8/>
<Gender9/>
<Gender10/>
<Race1>W</Race1>
<Race2>H</Race2>
<Race3/>
<Race4/>
<Race5/>
<Race6/>
<Race7/>
<Race8/>
<Race9/>
<Race10/>
</Demographic_Information>
<!--</...other XML Data>-->
</Data>
<!--</...more XML Data>-->
</Subjects>
</Root>
...必要な結果が生成されます。
<Root>
<Subjects>
<!--<...more XML Data>-->
<Data>
<!--<...other XML Data>-->
<Demographic_Information>
<Person subject="1">
<Age>33</Age>
<Gender>M</Gender>
<Race>W</Race>
</Person>
<Person subject="2">
<Age>66</Age>
<Gender>F</Gender>
<Race>H</Race>
</Person>
</Demographic_Information>
<!--</...other XML Data>-->
</Data>
<!--</...more XML Data>-->
</Subjects>
</Root>
説明:
<xsl:template>
各宣言から始めて、この手順を順を追って見ていきましょう。
テンプレート #1: ID テンプレート
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
このテンプレート (ちなみに、この回答と以前の回答との間の唯一の変更点です) には、すべてのノード (要素、テキスト ノード、コメント、および処理命令) とすべての属性をソース XML から結果の XML。したがって、そのままにしておくと、この 1 つのテンプレートで元の XML の正確なコピーが作成されます。このテンプレートを他のテンプレートでオーバーライドすることは、XSLT の最も基本的な設計パターンの 1 つです。
私たちの場合、このテンプレートは、直接一致しないノード/属性 (一部またはその子ではないすべてのノード/属性など<Demographic_Information>
) がそのまま結果ドキュメントにコピーされることを保証します。
テンプレート #2: 変更<Demographic_Information>
<xsl:template match="Demographic_Information">
<Demographic_Information>
<xsl:apply-templates
select="*[generate-id() =
generate-id(key(
'kElemByNumber',
translate(name(), translate(name(), $vNums, ''),
''
))[1])][normalize-space()]">
<xsl:sort
select="translate(name(), translate(name(), $vNums, ''), '')"
data-type="number"/>
</xsl:apply-templates>
</Demographic_Information>
</xsl:template>
このテンプレート (すべての<Demographic_Information>
要素に一致) は、Muenchian Groupingと呼ばれる手法を利用しています。これは、XSLT 1.0 で同様の要素をグループ化する方法です。この手法について詳しく説明するつもりはありません。SO で簡単に検索すると、手順を示すいくつかの 質問 が 表示されます。
Muenchian Grouping を理解したら、XSLT の上部で使用しているキーに注意してください。
<xsl:key
name="kElemByNumber"
match="Demographic_Information/*"
use="translate(name(), translate(name(), $vNums, ''), '')"/>
<Demographic_Information>
実際には、これは「名前の数値部分に基づいて のすべての子要素を教えてください」と言っています。したがって、すべての「1」要素がグループ化され、すべての「2」要素がグループ化されます。
「名前の数値部分」の部分は、特定の文字セットのみを保持する方法であるDouble Translate Methodを使用して実現されます。この場合、このメソッドを使用して、数値の文字のみをキーとして選択します。
テンプレート #3: の子要素を変更する<Demographic_Information>
<xsl:template match="Demographic_Information/*">
<Person subject="{position()}">
<xsl:apply-templates
select="key('kElemByNumber', position())"
mode="children">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</Person>
</xsl:template>
このテンプレートは前のテンプレートから実行されることに注意してください。したがって、これは Muenchian Grouping シーケンスの一部です。戦術的には、これは、このテンプレートが、数値部分が一意であるノードに対してのみ実行されることを意味します (つまり、最初の "1" 要素、最初の "2" 要素などに対して実行されます)。
の子要素が見つかる<Demographic_Information>
と、プロセッサは新しい要素を作成し、作業ツリー (この場合は のすべての子) 内の現在の位置の値を持つ属性を<Person>
与えるように指示されます。次に、正しい基準に一致するキー内のすべての要素 ( の子要素で構成されていることを思い出してください) にテンプレートを適用し、処理時に名前で並べ替えます。subject
<Demographic_Information>
<Demographic_Information>
この呼び出しmode="children"
に割り当てた属性に注意してください。<xsl:apply-templates>
これは、現在のセットアップで必要です: もしそれがなければ、無限ループを作成します ( の子に一致するテンプレートは、現在のテンプレートに一致する の<Demographic_Children>
すべての子に対してテンプレートを適用するように指示されます)。 <Demographic_Information>
.)。
テンプレート #4: の子要素の変更<Demographic_Information>
(「子」モード)
<xsl:template match="Demographic_Information/*" mode="children">
<xsl:element name="{translate(name(), $vNums, '')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<Demographic_Information>
これはかなり単純です: (モードの下で) foundのすべての子に対してchildren
、新しい要素が作成され、現在の要素と同じ名前が付けられますが、数値部分は削除されます。最後に、現在のノードの残っている子にテンプレートを適用することによって、この要素のコンテンツが取り込まれます (この場合、text()
ノードのみが残ります。したがって、<xsl:apply-templates>
テキストを出力するプロセッサの既定のテンプレートが使用されます)。これにより、最終出力が生成されます。
この説明があっても、ここでは多くのことが行われています。私が提供できる明確さが他にあれば教えてください。