xslt跟随组

时间:2011-08-19 18:26:01

标签: xslt

旧源XML:

<Employees>
    <Person>
      <FirstName>Joy</FirstName>
      <IsManager>N</IsManager>
    </Person>
    <Person>
      <FirstName>Joyce</FirstName>
      <IsManager>N</IsManager>
    </Person>
    <Person>
      <FirstName>Joe</FirstName>
      <IsManager>Y</IsManager>
    </Person>...
</Employees>

新源XML:

<Employees>
        <Person>
        <FirstName>Joy</FirstName>
        <DetailsArray>
            <Details1>
            <IsManager>N</IsManager>
            <IsSuperviser>N</IsSuperviser>
            </Details1>
        <Details2>
                <IsManager>N</IsManager>
            <IsSuperviser>N</IsSuperviser>
            </Details2>
        </DetailsArray>
        </Person>
        <Person>
        <FirstName>Joyce</FirstName>
        <DetailsArray>
            <Details1>
            <IsManager>N</IsManager>
            <IsSuperviser>N</IsSuperviser>
            </Details1>
        <Details2>
                <IsManager>N</IsManager>
            <IsSuperviser>N</IsSuperviser>
            </Details2>
        </DetailsArray>
        </Person>
        <Person>
        <FirstName>Joe</FirstName>
        <DetailsArray>
            <Details1>
            <IsManager>N</IsManager>
            <IsSuperviser>N</IsSuperviser>
            </Details1>
        <Details2>
                <IsManager>Y</IsManager>
            <IsSuperviser>N</IsSuperviser>
            </Details2>
    </DetailsArray>
        </Person>...
</Employees>
输出应该是:

    <Names>
    <Name num='1'>Joe</Name>
    <Name num='2'>Joy</Name>
    <Name num='3'>Joyce</Name>
    ....
    </Names>

与以前的XML相比,此源XML有一些调整。这里的新条件是“人可能链接到2个项目或2个任务”,所以我需要输出从IsManager ='Y'的人开始,即使IsManager在DetailsArray的Details2标签中是'y'。输出不应该有名称的重复。假设我们排序名称将被重复..

感谢您以前的答案..

3 个答案:

答案 0 :(得分:2)

修改即可。正如lwburk指出的那样,这个答案的原始解决方案只是按IsManager对节点进行排序。

这是一个解决方案,找到第一个经理,打印出来,然后循环通过剩下的人(如果需要,骑自行车回到开头)。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="Employees">

    <xsl:variable name="position" select="count(Person) - count(Person/IsManager[. = 'Y'][1]/../following-sibling::*)" />

    <xsl:call-template name="person">
        <xsl:with-param name="name" select="Person/IsManager[. = 'Y'][1]/../FirstName" />
        <xsl:with-param name="position" select="'1'" />
    </xsl:call-template>

    <xsl:for-each select="Person[position() &gt; $position]">            
            <xsl:call-template name="person" />
    </xsl:for-each>

    <xsl:for-each select="Person[position() &lt; $position]">
            <xsl:call-template name="person" />
    </xsl:for-each>
</xsl:template>

<xsl:template name="person">
    <xsl:param name="name" select="FirstName" />
    <xsl:param name="position" select="position() + 1" />   

    <Name>
        <xsl:attribute name="num"><xsl:value-of select="$position" /></xsl:attribute>
        <xsl:value-of select="$name" />
    </Name>
</xsl:template>

</xsl:stylesheet>

旧答案

我不确定你的问题,但我想你想从IsManager = Y的人那里得到所有的名字。您可以使用<xsl:sort> IsManager值。不要忘记在属性“order”中指定“descending”(否则,IsManager = Y的人将是最后一个)。

我写了一个与输入数据一起使用的示例:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="Employees">
    <xsl:for-each select="Person">
        <xsl:sort select="IsManager" order="descending" />

            <Name>
                <xsl:attribute name="num">
                    <xsl:value-of select="position()" />
                </xsl:attribute>
                <xsl:value-of select="FirstName" />

            </Name>

    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

这个简短而简单的转换(没有模式,没有变量,只有三个模板):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <Names>
    <xsl:apply-templates select="*/Person[IsManager='Y'][1]"/>
  </Names>
 </xsl:template>    

 <xsl:template match="Person[IsManager='Y']">
  <xsl:apply-templates select=
   "FirstName |../Person[not(generate-id()=generate-id(current()))]
                /FirstName
   ">
   <xsl:sort select=
    "generate-id(..) = generate-id(/*/*[IsManager = 'Y'][1])"
   order="descending"/>
   <xsl:sort select=
     "boolean(../preceding-sibling::Person[IsManager='Y'])"
     order="descending"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="FirstName">
  <Name num="{position()}"><xsl:value-of select="."/></Name>
 </xsl:template>
</xsl:stylesheet>

应用于以下XML (与@lwburk提供的相同):

<Employees>
    <Person>
        <FirstName>Joy</FirstName>
        <IsManager>N</IsManager>
    </Person>
    <Person>
        <FirstName>Joyce</FirstName>
        <IsManager>N</IsManager>
    </Person>
    <Person>
        <FirstName>Joe</FirstName>
        <IsManager>Y</IsManager>
    </Person>
    <Person>
        <FirstName>Professor X</FirstName>
        <IsManager>N</IsManager>
    </Person>
    <Person>
        <FirstName>Songey</FirstName>
        <IsManager>Y</IsManager>
    </Person>
</Employees>

生成想要的正确结果

<Names>
   <Name num="1">Joe</Name>
   <Name num="2">Professor X</Name>
   <Name num="3">Songey</Name>
   <Name num="4">Joy</Name>
   <Name num="5">Joyce</Name>
</Names>

解释

  1. 这是使用多个键进行排序的典型案例。

  2. 优先级最高的排序标准是Person父级是否是第一个经理。

  3. 第二个优先级排序标准是父人是否跟随经理。

  4. 我们使用以下事实:在false()之前排序布尔值true()时,我们按降序处理排序的节点列表。

答案 2 :(得分:0)

听起来你正试图从第一个管理器开始,然后按顺序处理所有Person元素,循环回到开头以获取分区元素之前的所有元素。

以下样式表实现了预期的结果:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:apply-templates select="Employees/Person"/>
    </xsl:template>
    <xsl:template match="Person[IsManager='Y'][1]">
        <Name num="1">
            <xsl:apply-templates select="FirstName"/>
        </Name>
        <!--  partition -->
        <xsl:apply-templates select="following-sibling::Person" mode="after"/>
        <xsl:apply-templates select="../Person" mode="before">
            <xsl:with-param name="pos" select="last() - position() + 1"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="Person" mode="after">
        <Name num="{position() + 1}">
            <xsl:apply-templates select="FirstName"/>
        </Name>
    </xsl:template>
    <xsl:template match="Person[not(IsManager='Y') and 
              not(preceding-sibling::Person[IsManager='Y'])]" mode="before">
        <xsl:param name="pos" select="0"/>
        <Name num="{position() + $pos}">
            <xsl:apply-templates select="FirstName"/>
        </Name>
    </xsl:template>
    <xsl:template match="Person"/>
    <xsl:template match="Person" mode="before"/>
</xsl:stylesheet>

注意: 1)此解决方案要求源中至少有一个经理; 2)这可能不是一个非常有效的解决方案,因为它需要多次传递并使用preceding-sibling来测试组成员资格(对于分区元素之前的元素)。

示例输入:

<Employees>
    <Person>
        <FirstName>Joy</FirstName>
        <IsManager>N</IsManager>
    </Person>
    <Person>
        <FirstName>Joyce</FirstName>
        <IsManager>N</IsManager>
    </Person>
    <Person>
        <FirstName>Joe</FirstName>
        <IsManager>Y</IsManager>
    </Person>
    <Person>
        <FirstName>Professor X</FirstName>
        <IsManager>N</IsManager>
    </Person>
    <Person>
        <FirstName>Songey</FirstName>
        <IsManager>Y</IsManager>
    </Person>
</Employees>

输出:

<Name num="1">Joe</Name>
<Name num="2">Professor X</Name>
<Name num="3">Songey</Name>
<Name num="4">Joy</Name>
<Name num="5">Joyce</Name>