【发布时间】:2013-01-25 03:45:37
【问题描述】:
XSLT 2.
嗨,我有一个 xml,它有 3 个节点,从“孩子”的角度命名:孩子、父亲和母亲父亲。从父亲节点开始,我需要根据子节点中的 ID 找到一个孩子的 MothersFather 节点(子节点是连接其他两个节点的中间参考。)
所以,对于每个父亲来说,都有他孩子的不同 MothersFather - 这些不是人类,一个父亲可能有数百个孩子,但只有大约二十个相关的 MothersFathers :)
XML 的简化版本(现实生活中大约有 80 个父亲节点、3000 个子节点和 400 个母亲父亲节点):
<t>
<Children>
<Child>
<ID>1</ID>
<FathersID>100</FathersID>
<MothersFatherID>200</MothersFatherID>
</Child>
<Child>
<ID>2</ID>
<FathersID>100</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
<Child>
<ID>3</ID>
<FathersID>100</FathersID>
<MothersFatherID>202</MothersFatherID>
</Child>
<Child>
<ID>4</ID>
<FathersID>100</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
<Child>
<ID>5</ID>
<FathersID>101</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
</Children>
<Fathers>
<Father>
<ID>100</ID>
</Father>
<Father>
<ID>101</ID>
</Father>
</Fathers>
<MothersFathers>
<MothersFather>
<ID>200</ID>
</MothersFather>
<MothersFather>
<ID>201</ID>
</MothersFather>
<MothersFather>
<ID>202</ID>
</MothersFather>
</MothersFathers>
</t>
我的 xslt 看起来像:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kFathersChildren" match="Child" use="FathersID"/>
<xsl:template match="/">
<xsl:apply-templates select="//Fathers"></xsl:apply-templates>
</xsl:template>
<xsl:template match="Fathers">
<xsl:apply-templates select="Father"></xsl:apply-templates>
</xsl:template>
<xsl:template match="Father">
<xsl:text> FATHER: ID=</xsl:text><xsl:value-of select="ID"/>
<!-- Now show all this fathers childrens maternal grandfathers based on the ID in the Child node -->
<!--TRY 1: this works, as in gets the right nodes, but doesn't do distinct values....-->
<xsl:for-each select="key('kFathersChildren', ID)"> <!-- get the fathers children -->
<xsl:text> found child: current MFid=</xsl:text><xsl:value-of select="current()/MothersFatherID"/>
<xsl:text> ID=</xsl:text><xsl:value-of select="ID"/>
<xsl:apply-templates select="//MothersFathers/MothersFather[ID=current()/MothersFatherID]"></xsl:apply-templates>
</xsl:for-each>
<!-- *** THIS IS WHERE I GET LOST??? - Do the same thing but only get distinct MothersFatherID's... -->
<!--TRY 2: note- won't compile in current state... -->
<xsl:for-each select="distinct-values(key('kFathersChildren', ID)[MothersFatherID])">
<xsl:text> Distinct MothersFatherID ???? - don't know what to select </xsl:text><xsl:value-of select="."/>
<xsl:apply-templates select="//MothersFathers/MothersFather[ID=??????????"></xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="//MothersFathers/MothersFather">
<xsl:text> IN MothersFather template... ID=</xsl:text><xsl:value-of select="ID"/>
</xsl:template>
</xsl:stylesheet>
在 Try 1 中,我可以获得所有节点和 MothersFatherID。 Try1 的输出是:
FATHER: ID=100
found child: current MFid=200 ID=1
IN MothersFather template... ID=200
found child: current MFid=201 ID=2
IN MothersFather template... ID=201
found child: current MFid=202 ID=3
IN MothersFather template... ID=202
found child: current MFid=201 ID=4
IN MothersFather template... ID=201
FATHER: ID=101
found child: current MFid=201 ID=5
IN MothersFather template... ID=201
在我选择“不同值”的 Try2 中,我希望输出如下:
FATHER: ID=100
IN MothersFather template... ID=201
IN MothersFather template... ID=200
IN MothersFather template... ID=202
FATHER: ID=101
IN MothersFather template... ID=201
(不是真正的输出 - 只是调试显示我可以引用正确节点的东西)。
但我不知道我要使用什么来引用唯一的 MothersFatherID 以传递给“应用模板”调用。
无论我尝试过什么,都会遇到以下错误:
Required item type of first operand of '/' is node(); supplied value has item type xs:anyAtomicType 或 Axis step child::element('':MothersFatherID) cannot be used here: the context item is an atomic value。我认为他们的意思是我正在尝试选择使用字符串值的节点,反之亦然....也许我对 distinct-value() 函数的使用完全错误?
谁能解释一下如何做到这一点? (我一直希望这个 xslt 在我不会被这种事情困住的时候会有一些禅意的时刻)。
此外,一旦我这样做了,我将希望 MothersFather 对每个父亲都按排序顺序排列 - 在真正的 xml 中,每个“ID”都有一个“名称” - 希望是每个“排序”声明将类似于参考什么修复上述问题?
感谢您的宝贵时间。 布莱斯。
编辑:
哇!!谢谢你的回答迪米特。我已经检查过了,希望你能帮我把它分解一下,因为我没有完全理解它? 答案是:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kMFByFId" match="MothersFatherID"
use="../FathersID"/>
<xsl:key name="kMFById" match="MothersFather" use="ID"/>
<xsl:key name="ChildByFIdAndMFId" match="Child"
use="concat(FathersID, '+', MothersFatherID)"/>
<xsl:template match="Children|MothersFathers|text()"/>
<xsl:template match="Father">
Father ID=<xsl:value-of select="ID"/>
<xsl:apply-templates select=
"key('kMFById',
key('kMFByFId', ID)
[generate-id(..)
=
generate-id(key('ChildByFIdAndMFId',
concat(../FathersID,'+',.)
)[1]
)
]
)">
<xsl:sort select="ID" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="MothersFather">
MothersFather ID=<xsl:value-of select="ID"/>
</xsl:template>
</xsl:stylesheet>
我可以使用所涉及的密钥。
<xsl:template match="Children|MothersFathers|text()"/> 这条线——这条线是怎么做的?如果我通过调试器单步执行它,它就会直接跳过这条线。如果我将其注释掉,就会有很多我看不到来源的多余输出。
还有为 MothersFather 节点提供 <xsl:apply-templates select= "key('kMFById', key('kMFByFId', ID)[generate-id(..) = 的 apply-templates 行——我一直试图在纸上将其分解以查看其神奇之处,但并没有完全理解它。
类似于
generate-id(key('ChildByFIdAndMFId', concat(../FathersID,'+',.))[1] ) ] )">key('kMFById', key('kMFByFId', ID) 的意思是通过当前的父亲 ID 获取匹配的 MothersFather 节点,其中[generate-id(..) 生成的 id 为 '(dot dot)' - 与父节点有关吗?哪一个?等于基于 ChildByFIdAndMFId 键 [1] 生成的 id - 这 1 是否只获得匹配生成的 id 的第一次出现,从而给出我的不同值?
(Dimitre 的这个答案也与 JLRishie 的答案非常相似。他的排序似乎有效,我是否遗漏了什么 Dimitre?)
问候,布莱斯。
【问题讨论】:
-
user1840734,JLRishe 的解决方案排序是否正确?是的,它现在正确排序。但是两天前当我发布我的答案时它没有正确排序。从那时起,他两次编辑了他的答案并修复了排序错误。至于“Dimitre 的这个答案也与 JLRishie 的答案非常相似”,我想说它们本质上是不同的,因为我的解决方案从一开始就可以正常工作,而 JLRishe 的排序不正确。与其他人不同,我总是测试我的解决方案。另一个区别在于简洁性和可读性——在这两个方面,我的解决方案显然更好。
-
@DimitreNovaatchev 我不确定你为什么一直说你的答案更短。我的是 23 行(最初是 20 行),而你的是 34 行。也许你的可显示字符少了 10 或 15 个,并且你将一些文本移到了左边,但这并不值得吹嘘。你暗示我没有测试我的答案,但我做了 - 我原始帖子中的输出是实际输出,并且与问题请求的输出相匹配。确实,我没有充分考虑能够在单独的字段上进行排序,但这是我不会再犯的疏忽。
-
@JLRishe,为什么,这不是很明显吗?您的转换占用了很长的行,使其不可读-我相信您可以将整个转换放在一行...当我以与我的格式相同的方式格式化您的转换时-为了使其可读,它占用的空间不23 行,但 37 行。而且,正如我们所知,37 > 34。如果我使用你的“格式化风格,我永远不会,我的答案将只是 19 行代码。除非你停止产生不可读的答案,否则它们对读者来说毫无价值,尽管我相信这些答案具有真正的价值——仅仅因为格式。
-
@DimitreNovaatchev 我猜你和我对什么是“不可读”和“无价值”有不同的概念。在普通的代码编辑器中,我的响应中的行是完全可显示的,并且在可读性范围内(恕我直言)。尽管如此,我会继续努力确保我的台词不会太长。
-
@JLRishe,“在普通的代码编辑器中,我的响应中的行完全可显示并且在可读性范围内”是不正确的。我正在使用 XSLT IDE(它的普通编辑器部分),甚至将整个屏幕分配给它,代码的很大一部分需要向右滚动才能显示。但是,向右滚动会使代码的最左侧部分不可见。您的代码向右移动了 147 个位置——这绝不是“完全可显示且在可读性范围内”。我欢迎您决心改进这一点。
标签: xslt xslt-2.0 xslt-grouping