This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: A little cross referencing problem


"Pywell, Andrew" <Andrew.Pywell@compaq.com> wrote:
> I have a little cross-referencing problem. My XML looks like this
> 
> <?xml version="1.0"?>
> <messageData>
>  <message dir="0" key="234" text="bla bla"/>
>  <message dir="1" key="235" text="more bla bla"/>
[snip]
>  <in key="240" status="File Error"/>
>  <in key="241" status="No Error"/>
>  <out key="235" status="No Error"/>
>  <out key="237" status="No Error"/>
> </messageData>
[snip]
> 
> If dir="0" then I need to find the matching "key" in <in> and extract the
> value of "status". Similarly if dir="1" the I need the "status" of the
> matching <out> record
> 
There are several solutions. One is to use xsl:choose to discriminate
whether the direction ist "In" or "Out":

  <?xml version="1.0"?>
  <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
    <xsl:output method="text" encoding="ISO-8859-1"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="message">
      <xsl:choose>
        <xsl:when test="@dir=0">
          <xsl:text>In </xsl:text>
          <xsl:value-of select="/*/in[@key=current()/@key]/@status"/>
        </xsl:when>
        <xsl:when test="@dir=1">
          <xsl:text>Out </xsl:text>
          <xsl:value-of select="/*/out[@key=current()/@key]/@status"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">Error in data</xsl:message>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:text> </xsl:text>
      <xsl:value-of select="@text"/>
      <xsl:text>&#xD;&#xA;</xsl:text>
    </xsl:template>
  </xsl:stylesheet>

Not that the code above will complain if the key attribute of a
message is illegal, but will not notice if there are no or more
than one matching in/out elements.

You can have more fun with keys, and perhaps some performance
gain:

  <?xml version="1.0"?>
  <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
    <xsl:output method="text" encoding="ISO-8859-1"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="in" match="in" use="@key"/>
    <xsl:key name="out" match="out" use="@key"/>

    <xsl:template match="message">
      <xsl:choose>
        <xsl:when test="@dir=0">
          <xsl:text>In </xsl:text>
          <xsl:value-of select="key('in',@key)/@status"/>
        </xsl:when>
        <xsl:when test="@dir=1">
          <xsl:text>Out </xsl:text>
          <xsl:value-of select="key('out',@key)/@status"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">Error in data</xsl:message>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:text> </xsl:text>
      <xsl:value-of select="@text"/>
      <xsl:text>&#xD;&#xA;</xsl:text>
    </xsl:template>
  </xsl:stylesheet>

Now for even more fun and some obfuscation, you can select the
key dynamically

  <?xml version="1.0"?>
  <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
    <xsl:output method="text" encoding="ISO-8859-1"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="k0" match="in" use="@key"/>
    <xsl:key name="k1" match="out" use="@key"/>

    <xsl:template match="message">
      <xsl:apply-templates select="key(concat('k',@dir),@key)"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="@text"/>
      <xsl:text>&#xD;&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="in">
      <xsl:text>In </xsl:text>
      <xsl:value-of select="@status"/>
    </xsl:template>
    <xsl:template match="out">
      <xsl:text>Out </xsl:text>
      <xsl:value-of select="@status"/>
    </xsl:template>
    <xsl:template match="/">
      <xsl:apply-templates select="*/message"/>
    </xsl:template>
  </xsl:stylesheet>

Think about why there is suddenly a template matching "/" necessary.

The code above will get a nasty runtime error if a dir attribute contains
an illegal value. This leads to my favorite piece of code which guards
the invocation of the dynamically selected key by having a key lookup in
the stylesheet whether the key actually exists.

    <xsl:key name="key" match="xsl:key" use="@name"/>
    <xsl:template match="message">
      <xsl:variable name="key" select="concat('k',@dir)"/>
      <xsl:choose>
        <xsl:when test="document('')[key('key',$key)]">
          <xsl:apply-templates select="key($key,@key)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">Error in data</xsl:message>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:text> </xsl:text>
      <xsl:value-of select="@text"/>
      <xsl:text>&#xD;&#xA;</xsl:text>
    </xsl:template>

I hope you didn't get an overdose of keys by now :-)

Have fun!
J.Pietschmann

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]