[英]How to group elements by content (XSLT 2.0)?
- 修改后的问题 -
非常感谢所有提供潜在解决方案的人,但这些都符合我的尝试,所以我认为我应该更清楚。 我稍微扩展了XML以使问题更加透明。
XML实际上是包含翻译内容的各种文件的汇编,目的是获得一个统一的文档,其中只包含唯一的英文字符串,并且(在人工审查和清理之后)每个字符串都有一个翻译的文件,因此它可以是用于翻译记忆库。 这就是为什么它现在是一个包含大量冗余信息的大文件。
每个para行包含英语母版(可在文件中重复几十次)和翻译变体。 在某些情况下,它很容易,因为所有翻译版本都是相同的,所以我最终会得到一行,但在其他情况下,它可能会更复杂。
因此,假设今天我有10个包含相同英语内容(#1),2个不同的德语变体,3个不同的法语变体,以及其他语言环境只需要获得的一个变体:
1 Para:1 EN / 2 DE(v1和v2)/ 3 FR(v1,v2和v3)/ ...
对于我的列表中的每个分组的唯一英语值,重复此操作
修改后的XML:
<Books>
<!--First English String (#1) with number of potential translations -->
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<FR>French Trans of #1 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v2</DE>
<FR>French Trans of #1 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<FR>French Trans of #1 v2</FR>
<!-- More locales here -->
</Para>
<!--Second English String (#2) with number of potential translations -->
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v1</DE>
<FR>French Trans of #2 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v3</DE>
<FR>French Trans of #2 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v2</DE>
<FR>French Trans of #2 v1</FR>
<!-- More locales here -->
</Para>
<!--Loads of additional English Strings (#3 ~ #n) with number of potential translations -->
目前的解决方案为我提供以下输出
<Books>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<DE>German Trans of #1 v2</DE>
<DE>German Trans of #2 v1</DE>
<DE>German Trans of #2 v3</DE>
<DE>German Trans of #2 v2</DE>
<FR>French Trans of #1 v1</FR>
<FR>French Trans of #1 v1</FR>
<FR>French Trans of #1 v2</FR>
<FR>French Trans of #2 v1</FR>
</Para>
</Books>
因此,只取第一个EN标签,然后将所有其他标签分组,与英语主字符串之间的差异无关。 虽然我的目标是获得以下内容:
<Books>
<!-- First Grouped EN string and linked grouped translations -->
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<DE>German Trans of #1 v2</DE>
<FR>French Trans of #1 v1</FR>
<FR>French Trans of #1 v2</FR>
</Para>
<!-- Second Grouped EN string and linked grouped translations -->
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v1</DE>
<DE>German Trans of #2 v3</DE>
<DE>German Trans of #2 v2</DE>
<FR>French Trans of #2 v1</FR>
</Para>
<!-- 3d to n Grouped EN string and linked grouped translations -->
</Books>
扩展了XSLT 2.0,以满足问题请求中的更新
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="Books">
<xsl:copy>
<xsl:for-each-group select="*"
group-by="EN">
<xsl:copy>
<xsl:copy-of select="EN"/>
<xsl:for-each-group select="current-group()/*[not(local-name()='EN')]"
group-by=".">
<xsl:sort select="local-name()"/>
<xsl:copy-of select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
扩展XSLT 1.0答案以满足问题请求中的更新
即使您需要两种不同类型的密钥,您仍然可以使用相同类型的解决方案。 这是第一个容易想到的解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="main" match="Para" use="EN"/>
<xsl:key name="locale" match="Para/*[not(self::EN)]" use="concat(../EN,.)"/>
<xsl:template match="Books">
<xsl:copy>
<xsl:apply-templates select="Para[
generate-id()
= generate-id(key('main',EN)[1])]" mode="EN"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="EN">
<xsl:copy>
<xsl:copy-of select="EN"/>
<xsl:apply-templates select="../Para/*[
generate-id()
= generate-id(key('locale',concat(current()/EN,.))[1])]" mode="locale">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="locale">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
适用时o
n the new provided input, produces:
<Books>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<DE>German Trans of #1 v2</DE>
<FR>French Trans of #1 v1</FR>
<FR>French Trans of #1 v2</FR>
</Para>
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v1</DE>
<DE>German Trans of #2 v3</DE>
<DE>German Trans of #2 v2</DE>
<FR>French Trans of #2 v1</FR>
</Para>
</Books>
这个XSLT 1.0转换完全符合您的要求,如果您愿意,它可以用作创建更有意义的结果树的起点:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="locale" match="Para/*[not(local-name()='EN')]" use="text()"/>
<xsl:template match="Books">
<xsl:copy>
<Para>
<xsl:copy-of select="Para[1]/EN"/>
<xsl:apply-templates select="Para/*[
generate-id()
= generate-id(key('locale',text())[1])]" mode="group">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
</Para>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="group">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
说明:
xsl:key
用于按内容对所有元素进行分组(但是EN
) PARA/EN
节点 xsl:sort
进行分组的方法 xsl:sort
以输出按请求分组的其他元素(具有相同内容的元素报告一次) 当应用于问题中提供的输入时,结果树是:
<Books>
<Para>
<EN>Some English Content</EN>
<DE>German Trans v1</DE>
<DE>German Trans v2</DE>
<FR>French Trans v1</FR>
<FR>French Trans v2</FR>
</Para>
</Books>
使用XSLT 2.0 xsl:for-each-group
相同结果(和更短的转换):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="Books">
<xsl:copy>
<Para>
<xsl:copy-of select="Para[1]/EN"/>
<xsl:for-each-group select="Para/*[not(local-name()='EN')]"
group-by=".">
<xsl:sort select="local-name()"/>
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:for-each-group>
</Para>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这种转变 :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kLangByValAndText"
match="Para/*[not(self::EN)]"
use="concat(name(), '+++', .)"/>
<xsl:template match="/">
<Books>
<Para>
<xsl:copy-of select="/*/Para[1]/EN"/>
<xsl:for-each select=
"/*/*/*[generate-id()
=
generate-id(key('kLangByValAndText',
concat(name(), '+++', .)
)
[1]
)
]
">
<xsl:sort select="name()"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</Para>
</Books>
</xsl:template>
</xsl:stylesheet>
当应用于此XML文档时 (提供的文档的扩展版本使其更有趣):
<Books>
<Para>
<EN>Some English Content</EN>
<DE>German Trans v1</DE>
<FR>French Trans v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>Some English Content</EN>
<EN-US>Some English Content</EN-US>
<DE>German Trans v1</DE>
<FR>French Trans v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>Some English Content</EN>
<Australian>Some English Content</Australian>
<DE>German Trans v1</DE>
<FR>French Trans v2</FR>
<!-- More locales here -->
</Para>
<!-- Much more para's hereafter containing variety of <EN> Content -->
</Books>
产生想要的,正确的结果 :
<Books>
<Para>
<EN>Some English Content</EN>
<Australian>Some English Content</Australian>
<DE>German Trans v1</DE>
<EN-US>Some English Content</EN-US>
<FR>French Trans v1</FR>
<FR>French Trans v2</FR>
</Para>
</Books>
说明 :Muenchian对复合(2部分)键进行分组。
请注意 :仅对翻译进行分组(如在此问题的另一个答案中所做的那样)丢失<Australian>
翻译 - 将@empo的解决方案应用于同一文档,结果是( <Australian>
丢失!):
<Books>
<Para>
<EN>Some English Content</EN>
<DE>German Trans v1</DE>
<EN-US>Some English Content</EN-US>
<FR>French Trans v1</FR>
<FR>French Trans v2</FR>
</Para>
</Books>
另一个muenchian分组,具有子级别的复合键:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:key name="english" match="EN" use="." />
<xsl:key name="others" match="Para/*[not(self::EN)]" use="concat(../EN, ' ', ., ' ', name())" />
<xsl:template match="/Books">
<Books>
<xsl:for-each select="Para/EN[generate-id() = generate-id(key('english', .)[1])]">
<Para>
<xsl:copy-of select=".|key('english', .)/../*[not(self::EN)][generate-id() = generate-id(key('others', concat(current(), ' ', ., ' ', name()))[1])]" />
</Para>
</xsl:for-each>
</Books>
</xsl:template>
</xsl:stylesheet>
使用Saxon 9时,我应用样式表
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="Books">
<xsl:copy>
<xsl:for-each-group select="Para" group-by="EN">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="Para">
<xsl:copy>
<xsl:copy-of select="EN"/>
<xsl:for-each-group select="current-group()/(* except EN)" group-by="node-name(.)">
<xsl:for-each-group select="current-group()" group-by=".">
<xsl:copy-of select="."/>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
对输入
<Books>
<!--First English String (#1) with number of potential translations -->
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<FR>French Trans of #1 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v2</DE>
<FR>French Trans of #1 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<FR>French Trans of #1 v2</FR>
<!-- More locales here -->
</Para>
<!--Second English String (#2) with number of potential translations -->
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v1</DE>
<FR>French Trans of #2 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v3</DE>
<FR>French Trans of #2 v1</FR>
<!-- More locales here -->
</Para>
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v2</DE>
<FR>French Trans of #2 v1</FR>
<!-- More locales here -->
</Para>
</Books>
我得到了结果
<Books>
<Para>
<EN>English Content #1</EN>
<DE>German Trans of #1 v1</DE>
<DE>German Trans of #1 v2</DE>
<FR>French Trans of #1 v1</FR>
<FR>French Trans of #1 v2</FR>
</Para>
<Para>
<EN>English Content #2</EN>
<DE>German Trans of #2 v1</DE>
<DE>German Trans of #2 v3</DE>
<DE>German Trans of #2 v2</DE>
<FR>French Trans of #2 v1</FR>
</Para>
</Books>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.