我有这个XML文件:
<Elements>
<Element name="A.B.C.x">
<Child>...</Child>
<Child>...</Child>
<Child>...</Child>
</Element>
<Element name="A.B.C.y">
<Child>...</Child>
<Child>...</Child>
</Element>
<Element name="A.D.E.y">
<Child>...</Child>
</Element>
<Element name="A.D.E.z">
<Child>...</Child>
<Child>...</Child>
<Child>...</Child>
</Element>
</Elements>
我需要创建XSL来获得这个结果:
<Elements>
<Element name="A.B.C">
<LastToken name="x" childCount="3" />
<LastToken name="y" childCount="2" />
</Element>
<Element name="A.D.E">
<LastToken name="y" childCount="1" />
<LastToken name="z" childCount="3" />
</Element>
</Elements>
我仅限于没有扩展的XSL 1.0,我无法弄清楚如何实现结果。
任何帮助表示赞赏。
提前致谢。
编辑:当我看到一些答案时,我必须澄清我的问题/任务:
name
节点的Element
属性中的标记不限于一个字符。 “name”属性的示例值可以是This.Is.Grouping.Target.AndThisIsGroupChild
答案 0 :(得分:2)
此XSLT 1.0转换(绝对没有限制):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kElemByName" match="Element"
use="@name"/>
<xsl:key name="klastTokenByName" match="@lastToken"
use="../@name"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Element/@name">
<xsl:attribute name="name">
<xsl:call-template name="init"/>
</xsl:attribute>
<xsl:attribute name="lastToken">
<xsl:call-template name="lastToken"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:apply-templates mode="pass2"
select="ext:node-set($vrtfPass1)/*"/>
</xsl:template>
<xsl:template mode="pass2" match="Element"/>
<xsl:template mode="pass2" match=
"Element[generate-id()
=
generate-id(key('kElemByName',@name)[1])
]
">
<Element name="{@name}">
<xsl:for-each select=
"key('klastTokenByName',@name)">
<lastToken name="{.}"
childCount="{count(key('kElemByName',../@name)
[@lastToken=current()]
/Child
)
}"
/>
</xsl:for-each>
</Element>
</xsl:template>
<xsl:template name="lastToken">
<xsl:param name="pText" select="."/>
<xsl:param name="pDelim" select="'.'"/>
<xsl:variable name="vrtfTokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select=
"ext:node-set($vrtfTokens)/*[last()]"/>
</xsl:template>
<xsl:template name="init">
<xsl:param name="pText" select="."/>
<xsl:param name="pDelim" select="'.'"/>
<xsl:variable name="vLastToken">
<xsl:call-template name="lastToken">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select=
"substring($pText,
1,
string-length($pText)
- string-length($vLastToken)
- string-length($pDelim)
)
"/>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pText"/>
<xsl:param name="pDelim" select="'.'"/>
<xsl:if test="string-length($pText)">
<token>
<xsl:value-of select=
"substring-before(concat($pText,$pDelim),
$pDelim)"/>
</token>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText,$pDelim)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<Elements>
<Element name="A.B.C.x">
<Child>...</Child>
<Child>...</Child>
<Child>...</Child>
</Element>
<Element name="A.B.C.y">
<Child>...</Child>
<Child>...</Child>
</Element>
<Element name="A.D.E.y">
<Child>...</Child>
</Element>
<Element name="A.D.E.z">
<Child>...</Child>
<Child>...</Child>
<Child>...</Child>
</Element>
</Elements>
产生了想要的正确结果:
<Elements>
<Element name="A.B.C">
<lastToken name="x" childCount="3"/>
<lastToken name="y" childCount="2"/>
</Element>
<Element name="A.D.E">
<lastToken name="y" childCount="1"/>
<lastToken name="z" childCount="3"/>
</Element>
</Elements>
答案 1 :(得分:1)
XSLT 2.0解决方案(绝对没有任何限制):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="xs my"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:for-each-group select="/*/*"
group-by="substring(@name, 1,
string-length(@name)
- string-length(my:LastToken(@name)) -1)">
<xsl:variable name="vLastToken"
select="my:LastToken(@name)"/>
<Element name="{substring(@name,1,
string-length(@name)
-
string-length($vLastToken)-1)}">
<xsl:for-each-group select="current-group()"
group-by="my:LastToken(@name)">
<xsl:variable name="vLastToken" select="my:LastToken(@name)"/>
<LastToken name="{$vLastToken}"
childCount="{count(current-group()/Child)}"/>
</xsl:for-each-group>
</Element>
</xsl:for-each-group>
</xsl:template>
<xsl:function name="my:LastToken" as="xs:string">
<xsl:param name="pText" as="xs:string"/>
<xsl:sequence select="tokenize($pText, '\.')[last()]"/>
</xsl:function>
</xsl:stylesheet>
应用于提供的XML文档时:
<Elements>
<Element name="A.B.C.x">
<Child>...</Child>
<Child>...</Child>
<Child>...</Child>
</Element>
<Element name="A.B.C.y">
<Child>...</Child>
<Child>...</Child>
</Element>
<Element name="A.D.E.y">
<Child>...</Child>
</Element>
<Element name="A.D.E.z">
<Child>...</Child>
<Child>...</Child>
<Child>...</Child>
</Element>
</Elements>
产生了想要的正确结果:
<Element name="A.B.C">
<LastToken name="x" childCount="3"/>
<LastToken name="y" childCount="2"/>
</Element>
<Element name="A.D.E">
<LastToken name="y" childCount="1"/>
<LastToken name="z" childCount="3"/>
</Element>
答案 2 :(得分:1)
只是为了好玩,一个没有扩展的通用XSLT 1.0解决方案:
<!DOCTYPE xsl:stylesheet [
<!ENTITY key "
substring(
@name,
1,
string-length(
@name
)
- count(
document('')//node()[
not(
contains(
substring(
current()/@name,
string-length(
current()/@name
)
- position()
+ 1
),
'.'
)
)
]
)
- 1
)">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kElementByNamePrefix" match="Element" use="&key;"/>
<xsl:key name="kElementByName" match="Element" use="@name"/>
<xsl:template match="Element">
<xsl:variable name="vNamePrefix" select="&key;"/>
<xsl:variable name="vCurrentGroup"
select="key('kElementByNamePrefix',$vNamePrefix)"/>
<xsl:if test="generate-id() = generate-id($vCurrentGroup[1])">
<Element name="{$vNamePrefix}">
<xsl:apply-templates
select="$vCurrentGroup[
generate-id()
= generate-id(
key('kElementByName',@name)[1]
)
]"
mode="prefix">
<xsl:with-param name="pNamePrefix" select="$vNamePrefix"/>
</xsl:apply-templates>
</Element>
</xsl:if>
</xsl:template>
<xsl:template match="Element" mode="prefix">
<xsl:param name="pNamePrefix"/>
<LastToken name="{substring(substring-after(@name,$pNamePrefix),2)}"
childCount="{count(key('kElementByName',@name)/Child)}"/>
</xsl:template>
</xsl:stylesheet>
输出:
<Element name="A.B.C">
<LastToken name="x" childCount="3" />
<LastToken name="y" childCount="2" />
</Element>
<Element name="A.D.E">
<LastToken name="y" childCount="1" />
<LastToken name="z" childCount="3" />
</Element>
答案 3 :(得分:0)
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="k1" match="Element" use="substring(@name, 1, 5)"/>
<xsl:key name="k2" match="Element" use="@name"/>
<xsl:template match="Elements">
<xsl:copy>
<xsl:apply-templates select="Element[generate-id() = generate-id(key('k1', substring(@name, 1, 5))[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Element">
<Element name="{substring(@name, 1, 5)}">
<xsl:apply-templates select="key('k1', substring(@name, 1, 5))[generate-id() = generate-id(key('k2', @name)[1])]" mode="token">
<xsl:sort select="substring(@name, 7)"/>
</xsl:apply-templates>
</Element>
</xsl:template>
<xsl:template match="Element" mode="token">
<LastToken name="{substring(@name, 7)}" childCount="{count(key('k2', @name)/Child)}"/>
</xsl:template>
</xsl:stylesheet>