开发者

Find maximum value of all child elements and get it’s parent element in xslt

This question is related to this post Find maximum value of all child elements and get its parent element in XSLT. I asked my question incorrectly. Actually I need output litt开发者_如何学运维le different. I need to match site node id with worksite node and find out the person who worked more hours for that site.

<root>
    <Site id="S1">
        <othernodes></othernodes>
    </Site>
    <Site id="S2">
        <othernodes></othernodes>
    </Site>
    <Site id="S3">
        <othernodes></othernodes>
    </Site> 
    <WorkSite Person="P1" Site="S1">
        <Hours>8</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S2">
        <Hours>2</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S3">
        <Hours>20</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S1">
        <Hours>6</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S2">
        <Hours>10</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S3">
        <Hours>21</Hours>
    </WorkSite>
</root>

The transformed content should be like this

<root>
    <site id="S1">
            <othernodes></othernodes>
            <person>P1</person>
    </site>
    <site id="S2">
            <othernodes></othernodes>
            <person>P2</person>
    </site>
    <site id="S3">
            <othernodes></othernodes>
            <person>P1</person>
    </site>
</root>

Can someone help on this?


Something like this?

<xsl:template match="/">
  <root>
    <xsl:apply-templates select="root/Site" />
  </root>
</xsl:template>

<xsl:template match="/root/Site">
  <xsl:variable name="sid" select="./@id" />
  <site id="{./@id}">
    <othernodes></othernodes>
    <xsl:apply-templates select="/root/WorkSite[@Site = $sid]">
      <xsl:sort select="./Hours" data-type="number" order="descending"/>
    </xsl:apply-templates>
  </site>
</xsl:template>

<xsl:template match="/root/WorkSite">
  <xsl:if test="position() = 1">
    <person><xsl:value-of select="./@Person" /></person>
  </xsl:if>
</xsl:template>


This XSLT 1.0 transformation:

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

 <xsl:key name="kSiteByName" match="@Site" use="."/>

 <xsl:key name="kWorksiteBySite"
   match="WorkSite" use="@Site"/>

 <xsl:key name="kSiteChildrenBySiteid"
  match="Site/node()" use="../@id"/>

 <xsl:variable name="vSites" select=
  "/*/*/@Site[generate-id()
             =
              generate-id(key('kSiteByName',.)[1])
              ]"
  />

 <xsl:template match="/">
  <root>
    <xsl:for-each select="$vSites">
      <xsl:for-each select="key('kWorksiteBySite', .)">
        <xsl:sort select="Hours" data-type="number"
         order="descending"/>
        <xsl:if test="position()=1">
         <site id="{@Site}">
            <xsl:copy-of select="key('kSiteChildrenBySiteid', @Site)"/>
            <person><xsl:value-of select="@Person"/></person>
         </site>
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </root>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <Site id="S1">
        <othernodes></othernodes>
    </Site>
    <Site id="S2">
        <othernodes></othernodes>
    </Site>
    <Site id="S3">
        <othernodes></othernodes>
    </Site>
    <WorkSite Person="P1" Site="S1">
        <Hours>8</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S2">
        <Hours>2</Hours>
    </WorkSite>
    <WorkSite Person="P1" Site="S3">
        <Hours>20</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S1">
        <Hours>6</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S2">
        <Hours>10</Hours>
    </WorkSite>
    <WorkSite Person="P2" Site="S3">
        <Hours>21</Hours>
    </WorkSite>
</root>

produces the wanted, correct result:

<root>
   <site id="S1">
      <othernodes/>
      <person>P1</person>
   </site>
   <site id="S2">
      <othernodes/>
      <person>P2</person>
   </site>
   <site id="S3">
      <othernodes/>
      <person>P2</person>
   </site>
</root>

Do note:

  1. The use of the Muenchian method for grouping to find all different Site values.

  2. The way maximum is found by sorting in descending order and getting the first result from the sorted node-list. This is much more efficient (O(N*log(N)) than scanning the sequence of nodes multiple times (comparing each value to every value in the list of values), which has O(N^2) time complexity.


This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="BySite" match="WorkSite" use="@Site"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Site/*[last()]">
        <xsl:call-template name="identity"/>
        <person>
            <xsl:value-of select="substring-after(
                                    key('BySite',../@id)
                                      [not(key('BySite',@Site)/Hours
                                           > Hours)]
                                      /@Person,
                                    'P')" />
        </person>
    </xsl:template>
    <xsl:template match="WorkSite"/>
</xsl:stylesheet>

Output:

<root>
    <Site id="S1">
        <othernodes></othernodes>
        <person>1</person>
    </Site>
    <Site id="S2">
        <othernodes></othernodes>
        <person>2</person>
    </Site>
    <Site id="S3">
        <othernodes></othernodes>
        <person>2</person>
    </Site>
</root>


This is what I would do. (I added the for-each just in case you had multiple people at a site with the exact same hours.)

EDIT: I didn't see the XSLT 1.0 requirement in the other post until after I added my answer. Sorry about that!

XML input:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <Site id="S1">
    <othernodes/>
  </Site>
  <Site id="S2">
    <othernodes/>
  </Site>
  <Site id="S3">
    <othernodes/>
  </Site>
  <WorkSite Person="P1" Site="S1">
    <Hours>8</Hours>
  </WorkSite>
  <WorkSite Person="P1" Site="S2">
    <Hours>2</Hours>
  </WorkSite>
  <WorkSite Person="P1" Site="S3">
    <Hours>20</Hours>
  </WorkSite>
  <WorkSite Person="P2" Site="S1">
    <Hours>6</Hours>
  </WorkSite>
  <WorkSite Person="P2" Site="S2">
    <Hours>10</Hours>
  </WorkSite>
  <WorkSite Person="P2" Site="S3">
    <Hours>21</Hours>
  </WorkSite>
</root>

XSLT 2.0 Stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Site">
    <xsl:variable name="vSiteId">
      <xsl:value-of select="@id"/>
    </xsl:variable>
    <xsl:variable name="vMaxHours">
      <xsl:value-of select="max(/root/WorkSite[@Site=$vSiteId]/Hours)"/>
    </xsl:variable>
    <site id="{@id}">
      <xsl:apply-templates/>
      <xsl:for-each select="/root/WorkSite[Hours=$vMaxHours]/@Person">
        <person>
          <xsl:value-of select="."/>
        </person>        
      </xsl:for-each>
    </site>
  </xsl:template>

  <xsl:template match="WorkSite"/>

</xsl:stylesheet>

XML output:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <site id="S1">
      <othernodes/>
      <person>P1</person>
   </site>
   <site id="S2">
      <othernodes/>
      <person>P2</person>
   </site>
   <site id="S3">
      <othernodes/>
      <person>P2</person>
   </site>
</root>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜