对于初学者我对标题的道歉,但我不知道描述我的问题的好方法。因此,代码示例将使事情变得更加清晰。
假设我有以下xml树:
<root>
<node>
<value xml:lang="en">Some English Content</value>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</node>
<node>
<value xml:lang="en">Some English Content</value>
<value xml:lang="de">Some German Content</value>
</node>
<node>
<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>
</node>
<node>
<value xml:lang="en">Some English Content</value>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</node>
<node>
<value xml:lang="fr">Some French Content</value>
<value xml:lang="de">Some German Content</value>
</node>
</root>
所以基本上有各种节点集,有许多本地化字符串,我想根据内容对这些集合进行分组。节点1,2,4和5是关于相同的主题,但并非所有语言环境中都可以使用所有字符串,因此我无法真正使用引用字符串(例如英语,因为它在节点5中不可用)。节点3包含不同的内容,因此它应该是不同组的一部分。
声音可能相当复杂,但这是我想要得到的结果(使用xslt 2):
<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>
有关如何最好地处理此问题的任何想法?请注意,我在一个节点中最多可以有40种不同的语言,并且文件中可能有数百个节点,因此资源也会成为一个问题。
答案 0 :(得分:0)
我不确定我是否完全理解您的要求,但我编写了一些代码,首先尝试使用node
填充value
个元素以查找缺少的语言,然后对填充的元素进行分组:< / p>
<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"
的第一个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>