Cross-referencing multiple documents with keys
Probably the most frustrating search this year has been my quest to find a good discussion on using keys in multiple source documents. Let me explain.
Keys are used to “tag” certain elements so that they can be referenced later. In a way, the <xsl:key> command functions like the \label{ } command in LaTeX. To call on a key (in LaTeX, that would be similar to the \ref{ } command), one uses key( ).
\section{Introduction}
\label{intro}\section{Theory}
This is a reference to Sec.\ \ref{intro}.
So in essence:
<xsl:key> -> \label{}
key() -> \ref{}
Note that in LaTeX, the arguments of both \label{} and \ref{} are the same if they reference the same item. So if a section of text is labeled, say, “intro” then the label declaration would be \label{intro} and the call to the section would be \ref{intro}. LaTeX generates an automatic number and substitutes it for the \ref{} call when it compiles the code.
The way the <xsl:key> command treats its arguments differs. The first glaring difference is that the source code of XML does not contain the key definitions, unlike LaTeX. In LaTeX, it would be similar to using the \label{} and \ref{} as arguments of the compiler call, telling the compiler what parts of the document the \label{} command defines and where the \ref{} command is called. Just to define the \label{} command alone would require something like the following in LaTeX: $latex -label{intro} at section{1}, subsection{2} (This won’t work, so don’t try it).
Let’s look at a sample XSL document that marks up two XML source documents:
<xsl:variable name=”StatesLookup” select=”document(’key_state.xml’)”/>
<xsl:key name=”StateKey” match=”state” use=”@sid”/>
<xsl:template match=”customer”>
<xsl:variable name=”custstate” select=”@stateid”/>
<xsl:variable name=”custname” select=”.”/>
<xsl:for-each select=”$StatesLookup”>
<xsl:value-of select=”$custname”/> lives in <xsl:value-of select=”key(’StateKey’, $custstate)”/>
</xsl:for-each>
</xsl:template>
Here is the first (primary) XML source document:
<customers>
<customer stateid=”03″>Bo</customer>
<customer stateid=”01″>Ty</customer>
<customer stateid=”02″>Jo</customer>
</customers>
Here is the second XML document:
<states region=”Midwest”>
<state sid=”01″>Iowa</state>
<state sid=”02″>Utah</state>
<state sid=”03″>Ohio</state>
</states>
Line 1 of the XSL file simply defines a variable to represent a call to the secondary document key_state.xml. So whenever we see a call to $StatesLookup we will be opening key_state.xml and peering in.
Line 2 defines the key we plan to use when marking up the two XML documents. It is strictly defined for elements of the secondary document key_state.xml, so at this point it functions like a \label{ } command placed inside a LaTeX section. The key pertains solely to any text element belonging to the @sid attribute/state element. In a way, defining the key is like creating a physical key to open all drawers of the file cabinet marked “state.” Each drawer is labeled individually “01,” “02,” and “03.”
Now that we have a key generated that will retrieve any of the text elements in the <state> elements, we have to have certain elements within the first XML (primary) document call on this key. We can’t insert the key( ) call within an XML document; key( ) commands are only found in XSL documents. Therefore, the key( ) is going to have to reference some portion of the primary XML document, which is why the key( ) command has two arguments: the first tells us which key we are using, the second tells us which element of the primary document is calling on the key. In line 7 of the XSL document, the call
key(’StateKey’, $custstate)
tells the style sheet to choose the key called StateKey and apply it to elements lying within the $custstate node, which is defined in Line 4. Since the stateid attribute has particular values for each <customer> element, these values get mapped into the key, thus referencing the appropriate element in the secondary XML document.
And it won’t just do this for the first element: The <xsl:for-each> command will apply the same key mappings for all elements in the <customers> node. In fact, the <xsl:for-each> command serves two purposes: It ensures that each element is processed and it shifts the target of the operations to the secondary document.
Tying it all together
So what does all this mean?
The key( ) command accepts two arguments: The first identifies the key and the second receives some value which is passed to the third argument of the <xsl:key> command, which then retrieves the corresponding value.
Next, I will need to learn how to generate automatic indexes.