xslt:通过中间参考节点选择唯一节点? [英] xslt: select unique node via intermediate reference node?

查看:35
本文介绍了xslt:通过中间参考节点选择唯一节点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

XSLT 2.

我有一个 xml,它有 3 个节点,从名为孩子"的角度命名:孩子、父亲和母亲父亲.从父节点开始,我需要根据子节点中的 ID 找到子节点的 MothersFather 节点(子节点是连接其他两个节点的中间引用.)

Hi, I have an xml that has 3 nodes, named from point of view of the 'children' called: Children, Fathers and MothersFathers. Starting with the Fathers node I need to find the a childs MothersFather node based on the ID's in the Child nodes (the Child node is the intermediate reference joining the other two.)

所以,对于每个父亲来说,他的孩子的独特 MothersFather - 这些不是人类,一个父亲可以有数百个孩子,但只有二十个左右的相关 MothersFathers :)

So, for each Father get his children's distinct MothersFather - these aren't humans, a father could have hundreds of children but only twenty or so of the related MothersFathers :)

XML 的简化版本(在现实生活中大约有 80 个父节点、3000 个子节点和 400 个 MothersFather 节点):

Simplified version of XML (in real life have about 80 Father nodes, 3000 Child nodes and 400 MothersFather nodes):

<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 看起来像:

My xslt looks like:

<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>&#10;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>&#10; 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>&#10;  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>&#10;      IN MothersFather template... ID=</xsl:text><xsl:value-of select="ID"/>
    </xsl:template>
</xsl:stylesheet>

在尝试 1 中,我可以获得所有节点和 MothersFatherID.Try1 的输出为:

In Try 1 I can get all the nodes and MothersFatherID's. The output of Try1 is:

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

在我选择distinct-value"的 Try2 中,我希望输出如下:

In Try2 where I'm selecting 'distinct-value' I would like output like:

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

(不是真正的输出 - 只是调试显示我可以引用正确节点的东西).

(is not real output - just debug stuff showing I can reference the right nodes).

但我不知道我要使用什么来引用唯一的 MothersFatherID 以传递给apply-templates"调用.

BUT I can't figure out what I'm meant to use to reference the unique MothersFatherID to pass to the 'apply-templates' call.

无论我尝试过什么,我都会遇到以下错误:'/' 的第一个操作数的必需项类型是 node();提供的值具有项目类型 xs:anyAtomicTypeAxis step child::element('':MothersFatherID) 不能在此处使用:上下文项目是原子值.我认为他们的意思是我正在尝试选择使用字符串值的节点,反之亦然......也许我使用 distinct-value() 函数是完全错误的?

No matter what I've tried I get variations on errors like: Required item type of first operand of '/' is node(); supplied value has item type xs:anyAtomicType or Axis step child::element('':MothersFatherID) cannot be used here: the context item is an atomic value. I think they mean I'm trying to select nodes where a string value is used or vice-versa.... maybe my use of distinct-value() function is altogether wrong?

谁能解释一下如何做到这一点?(我一直希望这个 xslt 能有一些禅意的时刻,当我不会被困在这种事情上时).

Can anyone shed some light on how to do this please? (I keep hoping this xslt will have some zen moment of enlightenment when I won't get stuck on this sort of thing).

此外,一旦我这样做了,我将希望 MothersFather 按照每个父亲的排序顺序 - 在真正的 xml 中,每个ID"都有一个名称" - 希望是每个排序"声明将类似参考解决上述问题的内容?

Additionally, once I have that going I'm going to want the MothersFather in a sorted order for each Father - in real xml there is a 'Name' associated with each 'ID' - hopefully the for-each 'sort' statement will be similar reference to what fixes above problem?

感谢您的时间.布莱斯.

Thanks for your time. Bryce.

哇!!谢谢你的回答迪米特.我已经看过了,希望你能帮我把它分解一下,因为我没有完全理解它?答案是:

Wow!! Thank you for your answer Dimitre. I have gone over it and was hoping you might be able to break it down a bit for me as I don't fully grok it? The answer was:

<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>

我可以使用所涉及的密钥.

I get the use of the keys involved.

<xsl:template match="Children|MothersFathers|text()"/> - 这行是怎么做的?如果我通过调试器单步执行,它只会直接跳过这条线.如果我把它注释掉,就会有很多我看不到来源的多余输出.

The line <xsl:template match="Children|MothersFathers|text()"/> - how is this line doing its thing? If I step it through a debugger it just jumps straight past this line. If I comment it out there is lots of superfluous output that I can't see the source of.

以及赋予 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) 表示通过当前的父亲 ID 获取匹配的 MothersFather 节点,其中 [generate-id(..) '(dot dot)' 的生成 id - 与父节点有关?哪个?等于基于 ChildByFIdAndMFId 键生成的 id [1] - 这个 1 是否只得到第一个匹配生成的 id 的出现从而赋予我独特的价值?

And the apply-templates line that gives the MothersFather node <xsl:apply-templates select= "key('kMFById', key('kMFByFId', ID)[generate-id(..) =
generate-id(key('ChildByFIdAndMFId', concat(../FathersID,'+',.))[1] ) ] )">
- I've been trying to break this down on paper to see the magic but not quite getting it. It is something like key('kMFById', key('kMFByFId', ID) means get the matching MothersFather nodes by the current Father ID where [generate-id(..) the generated id of '(dot dot)' - something to do with a parent node? which one? equals the generated id based on ChildByFIdAndMFId key [1] - does this 1 get only the first occurrence of the matching generated id's thereby giving my distinct value?

(Dimitre 的这个答案也与 JLRishie 的答案非常相似.他的排序似乎有效,我是否在 Dimitre 那里遗漏了什么?)

(This answer by Dimitre is also very similar to JLRishie's answer. His sort appears to work, am I missing something there 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>

当应用于这个 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 处理器正确执行转换.

The transformation is executed correctly both with an XSLT 1.0 and with XSLT 2.0 processor.

更新:

OP 已编辑问题,询问有关此解决方案的一些问题:

The OP has edited the question, asking some questions about this solution:

我可以使用所涉及的密钥.

I get the use of the keys involved.

<xsl:template match="Children|MothersFathers|text()"/> -这条线是如何做的?如果我通过调试器单步执行它直接跳过这条线.如果我评论它有很多我看不到来源的多余输出.

The line <xsl:template match="Children|MothersFathers|text()"/> - how is this line doing its thing? If I step it through a debugger it just jumps straight past this line. If I comment it out there is lots of superfluous output that I can't see the source of.

您已经发现了这个带有空体的模板正在做什么——它可以防止写入多余的输出.XSLT 处理器有许多内置模板,在处理给定节点时选择这些模板执行——以防 XSLT 转换没有指定与该节点匹配的模板.

You have discovered what this template with empty body is doing -- it prevents the superfluous output from being written. The XSLT processor has a number of built-in templates that are selected for execution when processing a given node -- in case the XSLT transformation doesn't specify a template matching this node.

任何元素的内置模板都会输出其所有文本节点后代的字符串值的串联——这正是您所看到的多余输出.

The built-in template for any element outputs the concatenation of the string values of all of its text-node-descendants -- and this is exactly what you see as superfluous output.

为了避免这种情况,我提供了一个模板匹配 thode 元素.这会覆盖(抑制)内置模板.由于此模板没有主体,因此不产生任何输出.

To avoid this, I have provided a template matching thode elements. This overrides (suppresses) the built-in template. As this tamplate has no body, no output is produced.

以及提供 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) 的意思是获取通过当前 Father ID 匹配 MothersFather 节点,其中[generate-id(..) '(dot dot)' 的生成 id - 要做的事情与父节点?哪一个?等于基于生成的 idChildByFIdAndMFId key [1] - 这个 1 是否只得到第一次出现匹配生成的 ID 从而赋予我独特的价值?

And the apply-templates line that gives the MothersFather node <xsl:apply-templates select= "key('kMFById', key('kMFByFId', ID)[generate-id(..) = generate-id(key('ChildByFIdAndMFId', concat(../FathersID,'+',.))[1] ) ] )"> - I've been trying to break this down on paper to see the magic but not quite getting it. It is something like key('kMFById', key('kMFByFId', ID) means get the matching MothersFather nodes by the current Father ID where [generate-id(..) the generated id of '(dot dot)' - something to do with a parent node? which one? equals the generated id based on ChildByFIdAndMFId key [1] - does this 1 get only the first occurrence of the matching generated id's thereby giving my distinct value?

您的问题是关于此代码片段的:

Your question is about this code fragment:

  <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>

为了了解这里发生了什么,您需要熟悉慕尼黑分组法.

In order to understand what is going on here, you need to get acquainted with the Muenchian Grouping Method.

上面代码片段的本质是:

处理所有 MothersFather 元素,这些元素是与 FathersID 具有相同值的第一个这样的元素当前 (Father) 节点.

process all MothersFather elements that are the first such element that is a sibling of a FathersID that has the same value as the ID of the current (Father) node.

这篇关于xslt:通过中间参考节点选择唯一节点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆