XSLT 2。
嗨,我有一个有3个节点的xml,从“孩子们”的角度命名:Children,Fathers and MothersFathers。从Fathers节点开始,我需要根据子节点中的ID找到子节点MothersFather节点(子节点是连接其他两个节点的中间节点。)
所以,每个父亲都会得到他孩子的独特 MothersFather--这些不是人类,父亲可能有数百个孩子,但只有20个左右的相关MothersFathers:)
XML的简化版本(在现实生活中有大约80个父节点,3000个子节点和400个MothersFather节点):
<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中,我选择'distinct-value',我希望输出如下:
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来传递给'apply-templates'。
无论我尝试过什么,我都会遇到以下错误:
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'都有一个'Name' - 希望for-each'sort'声明将类似地引用上面的问题修复?
感谢您的时间。 布莱斯。
修改
哇!!谢谢你的回答Dimitre。我已经过去了,并希望你能够为我分解一下,因为我没有完全理解它? 答案是:
<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为'(点点)' - 与父节点有关?哪一个?等于基于ChildByFIdAndMFId键生成的id [1]
- 这1只获得匹配生成的id的第一次出现,从而给出我的不同值吗?
(Dimitre的回答也与JLRishie的回答非常相似。他的那种似乎有效,我在Dimitre错过了什么吗?)
问候,布莱斯。
答案 0 :(得分:2)
我相信这应该做你想做的事情:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="kFathersChildren" match="Child"
use="concat(FathersID, ' - ', MothersFatherID)"/>
<xsl:key name="kChildByFatherId" match="Child" use="FathersID"/>
<xsl:key name="kMothersFatherById" match="MothersFather" use="ID" />
<xsl:template match="text() | Children | MothersFathers" />
<xsl:template match="Father">
<xsl:value-of select="concat(' FATHER: ID=', ID)" />
<xsl:apply-templates
select="key('kMothersFatherById',
key('kChildByFatherId', ID)
[generate-id() =
generate-id(
key('kFathersChildren',
concat(FathersID, ' - ', MothersFatherID)
)[1])
]/MothersFatherID)">
<xsl:sort select="ID" data-type="number" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="MothersFather">
<xsl:value-of select="concat(' IN MothersFather template... ID=', ID)"/>
</xsl:template>
</xsl:stylesheet>
在样本输入上运行时,会产生:
FATHER: ID=100
IN MothersFather template... ID=200
IN MothersFather template... ID=201
IN MothersFather template... ID=202
FATHER: ID=101
IN MothersFather template... ID=201
答案 1 :(得分:1)
此转换 - 更短,格式良好且可读,无需水平/垂直滚动。此外,与其他答案不同,它正确应用排序:
<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>
应用于此XML文档时(提供的,但稍微改组以测试正确的排序):
<t>
<Children>
<Child>
<ID>2</ID>
<FathersID>100</FathersID>
<MothersFatherID>201</MothersFatherID>
</Child>
<Child>
<ID>1</ID>
<FathersID>100</FathersID>
<MothersFatherID>200</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>
会产生想要的正确结果:
Father ID=100
MothersFather ID=200
MothersFather ID=201
MothersFather ID=202
Father ID=101
MothersFather ID=201
请注意:
使用XSLT 1.0和XSLT 2.0处理器正确执行转换。
<强>更新强>:
OP编辑了这个问题,询问了有关此解决方案的一些问题:
我使用了所涉及的密钥。
第
<xsl:template match="Children|MothersFathers|text()"/>
行 - 这条线怎么做呢?如果我通过调试器执行它 只是跳过这条线。如果我评论它有很多 多余的输出,我看不到它的来源。
你已经发现了这个带有空体的模板正在做什么 - 它可以防止写入多余的输出。 XSLT处理器具有许多内置模板,在处理给定节点时选择执行这些模板 - 以防XSLT转换未指定与此节点匹配的模板。
任何元素的内置模板都会输出所有text-node-descendants的字符串值的串联 - 这正是你所看到的多余输出。
为了避免这种情况,我提供了一个匹配thode元素的模板。这会覆盖(抑制)内置模板。由于此模板没有主体,因此不会产生任何输出。
提供
MothersFather
节点的apply-templates行<xsl:apply-templates select= "key('kMFById', key('kMFByFId',
ID)[generate-id(..) = generate-id(key('ChildByFIdAndMFId',
concat(../FathersID,'+',.))[1] ) ] )">
- 我一直试图打破 这是纸上看到的神奇,但并没有得到它。它是 像key('kMFById', key('kMFByFId', ID)
之类的东西意味着获得 按当前MothersFather
匹配Father ID
个节点[generate-id(..) the generated id of '(dot dot)'
- 要做的事情 与父节点?哪一个?等于基于生成的idChildByFIdAndMFId key [1]
- 此1
只会首次出现 匹配生成的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>
为了了解这里发生了什么,您需要熟悉 Muenchian Grouping Method 。
上面的代码片段基本上是:
处理所有MothersFather
个元素,这些元素是FathersID
的兄弟,与当前ID
的{{1}}具有相同的值Father
节点