Subject: How to resolve IDREF in XSL
Posted By: teahouse Post Date: 9/15/2006 11:55:52 AM
Hi,

I am a newbie to XML/XSLT. I searched on this forum, however did not
find exactly what I look up. Basically, I have two tables A and B,
A contains a REF (a foreign key to B), so when I display table A
using XSL, since I have the idref to B's entry, how can I extract
attribute from B?

For illustration purpose, I have a database DTD for tools and
customers, and sample tool.xml, and its corresponding tool.xsl.
How do I display the customer's name, instead of the current
refID (I'm using borrower/@bid) (which is cust1)? See all the file
below.

Thanks for help!

-Tao


================== tool.dtd ===============
<!ELEMENT database (tools|customers)*>

<!ELEMENT tools (tool)* >
<!ELEMENT tool (toolname, borrower*)>

<!ELEMENT toolname (#PCDATA)>
<!ELEMENT borrower EMPTY>
    <!ATTLIST borrower bid IDREF #REQUIRED>

<!ELEMENT customers (customer)*>
    <!ELEMENT customer (name, address) >
        <!ATTLIST customer cid ID #REQUIRED>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENT address (#PCDATA)>

================== tool.xml===============

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE database SYSTEM "tool.dtd">

<?xml-stylesheet type="text/xsl" href="tool.xsl"?>

<database>
    <tools>
    <tool>
    <toolname>Foo Bar</toolname>
    <borrower bid="cust1" />
    </tool>
    </tools>
    
    <customers>
    <customer cid="cust1">
    <name>Ben Jackson</name>
    <address>688 Adams Alley, Cincinnati, OH</address>
    </customer>
     </customers>

</database>
================== tool.xsl ===============
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" omit-xml-declaration="no" doctype-system="http://www.w3c.org/TR/xhtml/DTD/xhtml1-strict.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"/>

<xsl:template match="/"> <!-- match root element -->
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>This is a Tool Collection</title>
</head>

<body>
<h2>My Tool Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">Tool Name</th>
<th>Borrower ID</th>
<th>Borrower Name</th>
</tr>

<xsl:for-each select="database/tools/tool">
<tr>
<td>
<xsl:value-of select="toolname"/>
</td>
<td>
<xsl:value-of select="borrower/@bid"/>
</td>
</tr>
</xsl:for-each>
</table>

</body>
</html>
</xsl:template>
</xsl:stylesheet>


Reply By: joefawcett Reply Date: 9/15/2006 12:25:45 PM
There are at least three approaches, the first is to use something like:
<xsl:for-each select="database/tools/tool">
            <tr>
              <td>
                <xsl:value-of select="toolname"/>
              </td>
              <td>
                <xsl:value-of select="borrower/@bid"/>
              </td>
              <td>
                <xsl:value-of select="/*/customers/customer[@cid = current()/borrower/@bid]/name"/>
              </td>
            </tr>
          </xsl:for-each>

The current() function refers to the context node before the XPath expression is evaluated, alternatively you could store the value in a variable.

The better way to do this is to declare a key to access the customers.
  <xsl:key name="customerById" match="customer" use="@cid"/>
This needs to be outside of any xsl:template element.

You can then use this to retrieve customers:
<xsl:for-each select="database/tools/tool">
            <tr>
              <td>
                <xsl:value-of select="toolname"/>
              </td>
              <td>
                <xsl:value-of select="borrower/@bid"/>
              </td>
              <td>
                <xsl:value-of select="key('customerById', borrower/@bid)/name"/>
              </td>
            </tr>
          </xsl:for-each>

Thirdly, as you have defined @cid to be of type ID then you can use the id function:
<xsl:for-each select="database/tools/tool">
            <tr>
              <td>
                <xsl:value-of select="toolname"/>
              </td>
              <td>
                <xsl:value-of select="borrower/@bid"/>
              </td>
              <td>
                <xsl:value-of select="id(borrower/@bid)/name"/>
              </td>
            </tr>
          </xsl:for-each>


--

Joe (Microsoft MVP - XML)
Reply By: mhkay Reply Date: 9/15/2006 12:35:37 PM
You can use the id() function. If @ref is an IDREF attribute, then id(@ref) returns the element whose ID it contains.

This is rather dependent on ID's being notified to the XSLT processor. Sometimes this breaks down, for example if you add a preprocessing phase to your transformation pipeline. Some people therefore recommend using keys instead. Declare

<xsl:key name="customer-id" match="customer" use="id"/>

and then key('customer-id', @ref)

will give you the customer whose ID value is in @ref.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply By: teahouse Reply Date: 9/15/2006 1:14:42 PM
Joe/Michael,

Thank you so much for the prompt replies.
I'll try them out.

Thanks again!!!
-Tao

Go to topic 49822

Return to index page 173
Return to index page 172
Return to index page 171
Return to index page 170
Return to index page 169
Return to index page 168
Return to index page 167
Return to index page 166
Return to index page 165
Return to index page 164