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: Variable number of attributes


At 12:53 30/09/00 -0400, John E. Simpson wrote:
> [a solution to a problem involving a table of attributes]

It's worth noting that there could be a problem if the attributes are in
different orders for different elements.  The attribute:: axis goes through
the attributes in document order: if these are different for different
elements, then the attribute values will end up in the wrong cells.  It's
easy to get around this by sorting the attributes by name within the
xsl:for-eaches:

  <xsl:for-each select="@*">
    <xsl:sort select="name()" />
    <td><xsl:value-of select="."/></td>
  </xsl:for-each>

It's usually worth doing this because there's no automated way to check
whether attributes occur in a particular order (validation won't tell you)
and it's perfectly legal for a (multi-threaded) XML parser to go through
them in whatever order it wants - attributes are by definition unordered.

>(1) The below works as long as the <ele> elements each has the same number 
>of attributes, with the same names. If one <ele> has 4 attributes and one 
>has 3, for example, then it will break.

It will also break if different elements have different combinations of
attributes, e.g.:

<ele name1="A1" name2="A2" />
<ele name2="B2" name3="B3" />

Here, there are three attributes in total, but only two per element.

When you take this into account, the problem becomes one of firstly
identifying what uniquely named attributes there are within the XML source,
and then, for each element, emitting the value of the attributes in the
same order.

Identifying uniquely named attributes just involves a Muenchian approach -
defining a key that indexes on attribute name to group the attributes:

<xsl:key name="attrs" match="ele/@*" use="name()" />

and then finding those attributes that are first in the set returned by the
key for that particular name:

  /doc/ele/@*[generate-id() = generate-id(key('attrs', name())[1])]

These attributes can be stored in a (global) variable for future reference:

<xsl:variable name="attrs"
              select="/doc/ele/@*[generate-id() =
                                  generate-id(key('attrs', name())[1])]" />

Creating the header row is then a matter of cycling over these attributes
and giving their names:

<xsl:for-each select="$attrs">
  <th><xsl:value-of select="name()"/></th>
</xsl:for-each>

The body of the table involves cycling over these attributes as well, in
order to get the right number and right order for the cells, but this time
getting the value of the attribute on the 'ele' element you're interested
in.  Iterating over the attributes is easy:

<xsl:template match="ele">
  <tr>
    <xsl:for-each select="$attrs">
      <td>...</td>
    </xsl:for-each>
  </tr>
</xsl:template>

But within the xsl:for-each, the current node will be one of those
attributes, not the 'ele' element.  This means that you have to set a
variable to hold a reference to the 'ele' element so that you can access
the value of the relevant attribute within the xsl:for-each:

<xsl:template match="ele">
  <xsl:variable name="ele" select="." />
  <tr>
    <xsl:for-each select="$attrs">
      <td>...</td>
    </xsl:for-each>
  </tr>
</xsl:template>

The value that you want within the cell is then the value of the attribute
of the $ele element that has the same name as the name of the current node
(the attribute in $attrs that you're currently on):

<xsl:template match="ele">
  <xsl:variable name="ele" select="." />
  <tr>
    <xsl:for-each select="$attrs">
      <td><xsl:value-of select="$ele/@*[name() = name(current())]" /></td>
    </xsl:for-each>
  </tr>
</xsl:template>

I've tested this approach and it works in SAXON and Xalan.

This level of complexity is completely unnecessary for the problem as it
was phrased, but it demonstrates how much XSLT stylesheets are dependent on
assumptions that are made about the XML source that they're expected to
encounter.

Cheers,

Jeni

Jeni Tennison
http://www.jenitennison.com/


 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]