あなたの要件を完全に理解しているかどうかはわかりませんが、最初に不足している言語のnode
要素をs で埋め、次に埋められた要素をグループ化しようとするコードを書きました:value
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:param name="sep" as="xs:string" select="'|'"/>
<xsl:output indent="yes"/>
<xsl:variable name="main-doc" select="/"/>
<xsl:variable name="languages" as="xs:string*">
<xsl:perform-sort select="distinct-values(root/node/value/@xml:lang)">
<xsl:sort select="."/>
</xsl:perform-sort>
</xsl:variable>
<xsl:key name="k1" match="node/value" use="concat(@xml:lang, $sep, .)"/>
<xsl:template match="root">
<values>
<xsl:variable name="filled" as="element(node)*">
<xsl:apply-templates select="node" mode="fill"/>
</xsl:variable>
<xsl:for-each-group select="$filled" group-by="string-join(value, $sep)">
<group>
<xsl:copy-of select="value"/>
</group>
</xsl:for-each-group>
</values>
</xsl:template>
<xsl:template match="node" mode="fill">
<xsl:copy>
<xsl:variable name="this" as="element(node)" select="."/>
<xsl:for-each select="$languages">
<value xml:lang="{.}">
<xsl:value-of
select="if ($this/value[lang(current())])
then $this/value[lang(current())]
else (key('k1',
concat($this/value[1]/@xml:lang, $sep, $this/value[1]),
$main-doc)/../value[lang(current())])[1]"/>
</value>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
ご覧のとおり、要素を埋めるためにいくつかの順序が必要だったので、個別に並べ替えましたが、それが必要かどうかわかり@xml:lang
ません。そのアプローチでは、Saxon 9.5 で投稿した入力の出力は次のようになります。
<values>
<group>
<value xml:lang="de">Some German Content</value>
<value xml:lang="en">Some English Content</value>
<value xml:lang="fr">Some French Content</value>
</group>
<group>
<value xml:lang="de">Some Other German Content</value>
<value xml:lang="en">Some Other English Content</value>
<value xml:lang="fr">Some Other French Content</value>
</group>
</values>
また、要素を埋めるためにどの戦略が意図されているのかわかりません(私が投稿したコメントも参照してください)。node/value
最終的に、要素とそのコンテンツの連結にキーを設定することにしました@xml:lang
。次に、言語が欠落している要素を埋めるときに、ある最初のvalue
子に単純に一致させnode
ます。つまり、基本的には、一部node
に最初のvalue xml:lang="foo"
with contentsがあるbar
場合、一致は単にその言語foo
と内容に基づいており、欠落している言語bar
の内容をコピーすることを意味します。value
並べ替えが不要で、属性の最初のシーケンスの順序を維持できる場合は、並べ替えを省略できます。スタイルシートは次のようになります。
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:param name="sep" as="xs:string" select="'|'"/>
<xsl:output indent="yes"/>
<xsl:variable name="main-doc" select="/"/>
<xsl:variable name="languages" as="xs:string*" select="distinct-values(root/node/value/@xml:lang)"/>
<xsl:key name="k1" match="node/value" use="concat(@xml:lang, $sep, .)"/>
<xsl:template match="root">
<values>
<xsl:variable name="filled" as="element(node)*">
<xsl:apply-templates select="node" mode="fill"/>
</xsl:variable>
<xsl:for-each-group select="$filled" group-by="string-join(value, $sep)">
<group>
<xsl:copy-of select="value"/>
</group>
</xsl:for-each-group>
</values>
</xsl:template>
<xsl:template match="node" mode="fill">
<xsl:copy>
<xsl:variable name="this" as="element(node)" select="."/>
<xsl:for-each select="$languages">
<value xml:lang="{.}">
<xsl:value-of
select="if ($this/value[lang(current())])
then $this/value[lang(current())]
else (key('k1',
concat($this/value[1]/@xml:lang, $sep, $this/value[1]),
$main-doc)/../value[lang(current())])[1]"/>
</value>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
そうすれば、出力はあなたが求めたとおりになります。
<values>
<group>
<value xml:lang="en">Some English Content</value>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</group>
<group>
<value xml:lang="en">Some Other English Content</value>
<value xml:lang="fr">Some Other French Content</value>
<value xml:lang="de">Some Other German Content</value>
</group>
</values>