开发者

XPath results from XSLT are incorrect - not going through entire XML

So I have a really confusing problem on my hands..

It seems as though that the entire XML data hierarchy is not being searched through when using an XPath expression in XSL.

Some dummy XML data:

<pets name="myPets" NUM="2">
    <dog name="allMyDogs" NUM="5">
        <dog name="Frank"  NUM="3"/>开发者_Python百科;
        <dog name="Spot"  NUM="4"/>
        <dog name="Rover"  NUM="1"/>
        <dog name="Rupert" NUM="6"/>
        <cat name="Lucy"  NUM="4"/>
    </dog>
    <cat name="allMyCats" NUM="4">
        <cat name="Simba" NUM="4"/>
        <cat name="Princess"  NUM="5"/>
        <cat name="Fluffy" NUM="1"/>
        <cat name="Lucy"  NUM="3"/>
        <cat name="Lucy"  NUM="35"/>
        <cat name="Lucy"  NUM="6"/>
        <cat name="Lucy" NUM="1"/>
    </cat>
    <cat name="Lucy" NUM="9"/>
</pets>

The following is the portion of XSLT code I believe is causing the issue:

 <xsl:key name="elem_key" match="elem" use="concat(@key, .)" />

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:template match="//*[@NUM&lt;=4]">
    <elem key="{name()}">
      <xsl:copy-of select="@*" />
      <xsl:for-each select="@*">
        <xsl:sort select="name()" />
        <attribute>|<xsl:value-of select="name()" />|</attribute>
      </xsl:for-each>
    </elem>
  </xsl:template>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each select="msxsl:node-set($all_data)">
              <xsl:for-each select="*[generate-id()=generate-id(key('elem_key',concat(@key, .))[1])]">
                <table >
                  <tr>
                    <td>Element Name</td>
                    <xsl:for-each select="*">
                      <td>
                        <xsl:value-of select="translate(.,'|','')" />
                      </td>
                    </xsl:for-each>
                  </tr>
                  <xsl:for-each select="key('elem_key', concat(@key, .))">
                    <xsl:variable name="curr_elem" select="." />
                    <tr>
                      <td>
                        <xsl:value-of select="@key" />
                      </td>
                      <xsl:for-each select="*">
                        <td >
                          <xsl:value-of select="$curr_elem/@*[name()=translate(current(),'|','')]" />
                        </td>
                      </xsl:for-each>
                    </tr>
                  </xsl:for-each>
                </table>
                <p />
              </xsl:for-each>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

The XPath expression used:

//*[@NUM&lt;=4]

(Above should generate many results)

The incorrect results I get:

Element Name    name     NUM
pets            myPets   2

As you can see it seems to stop at the root.

If I change the XPath to:

//*[@NUM=4]

I get these incorrect results:

Element Name  name    NUM
dog        Spot      4


Element Name  name      NUM
cat        Lucy      4


Element Name  name      NUM
cat        allMyCats   4

What appears to happen is that it will stop searching down into the hierarchy once it has found a match. The first two (Spot and Lucy) are correct, but then it stopped at allMyCats, when there is a child node of allMyCats (Simba) that has a NUM of 4.

Can anyone help me fix this code so that it returns the correct results? I'm quite frustrated! :(

Thanks!


Just change:

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

To:

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*/*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

The first (of many) problem is:

<xsl:apply-templates select="*">

selects all children elements of the current node. The current node is / and it has only one child -- the top element pets.

You actually want to collect the data for all children of pets.

There are other problems, but I don't have the space and time to address them here.

The complete corrected code is below:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
version="1.0">

 <xsl:key name="elem_key" match="elem" 
      use="concat(@key, .)" />

  <xsl:variable name="all_data">
    <xsl:apply-templates select="*//*">
      <xsl:sort select="name()" />
    </xsl:apply-templates>
  </xsl:variable>

  <xsl:template match="*[@NUM&lt;=4]">
    <elem key="{name()}">
      <xsl:copy-of select="@*" />
      <xsl:for-each select="@*">
        <xsl:sort select="name()" />
        <attribute>|<xsl:value-of 
             select="concat(name(),'=',.)" />|</attribute>
      </xsl:for-each>
    </elem>
  </xsl:template>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each select="msxsl:node-set($all_data)">
              <xsl:for-each select=
               "*[generate-id()
                 =
                  generate-id(key('elem_key',concat(@key, .))[1])
                 ]">
                <table >
                  <tr>
                    <td>Element Name</td>
                    <xsl:for-each select="*">
                      <td>
                        <xsl:value-of select=
                          "substring-before(translate(.,'|',''),'=')" />
                      </td>
                    </xsl:for-each>
                  </tr>
                    <tr>
                      <td>
                        <xsl:value-of select="@key" />
                      </td>
                      <xsl:for-each select="*">
                        <td>
                          <xsl:value-of select=
                          "substring-after
                              (translate(current(),'|',''),
                               '='
                               )"/>
                        </td>
                      </xsl:for-each>
                    </tr>
                </table>
                <p />
              </xsl:for-each>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<pets name="myPets" NUM="2">
    <dog name="allMyDogs" NUM="5">
        <dog name="Frank"  NUM="3"/>
        <dog name="Spot"  NUM="4"/>
        <dog name="Rover"  NUM="1"/>
        <dog name="Rupert" NUM="6"/>
        <cat name="Lucy"  NUM="4"/>
    </dog>
    <cat name="allMyCats" NUM="4">
        <cat name="Simba" NUM="4"/>
        <cat name="Princess"  NUM="5"/>
        <cat name="Fluffy" NUM="1"/>
        <cat name="Lucy"  NUM="3"/>
        <cat name="Lucy"  NUM="35"/>
        <cat name="Lucy"  NUM="6"/>
        <cat name="Lucy" NUM="1"/>
    </cat>
    <cat name="Lucy" NUM="9"/>
</pets>

the wanted result (multiple animals) is produced:

<html xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <body>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Lucy</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>allMyCats</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Simba</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Fluffy</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Lucy</td>
                <td>3</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>cat</td>
                <td>Lucy</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Frank</td>
                <td>3</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Spot</td>
                <td>4</td>
            </tr>
        </table>
        <p></p>
        <table>
            <tr>
                <td>Element Name</td>
                <td>name</td>
                <td>NUM</td>
            </tr>
            <tr>
                <td>dog</td>
                <td>Rover</td>
                <td>1</td>
            </tr>
        </table>
        <p></p>
    </body>
</html>


*[generate-id()=generate-id(key('elem_key',concat(@key, .))[1])] You've a key doing a selection that matches a few cases, and then you ask for just the first with [1]

I'm not clear on what layout you are wanting as the correct results to advise further.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜