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: Newbie needs xsl


Hi Rosh,

> Thanks, this has been a tutorial for me. A question in line with
> this scenario - how do you propose applying templates if an
> element (office) forms sub-node as given below?

The template I showed you before was:

<xsl:template match="customer_name">
   <!-- Customer Name -->
   <xsl:text>Customer Name: </xsl:text><xsl:value-of select="@id" />
   <!-- Revenue -->
   <xsl:text>&#xA;   Revenue: $</xsl:text>
   <xsl:value-of select="@revenue div 100" />
   <!-- Office 1 -->
   <xsl:text>&#xA;   Office1: </xsl:text>
   <xsl:value-of select="substring-before(@office1, ' ')" />
   <xsl:text>sq feet</xsl:text>
   <!-- Office 2 -->
   <xsl:if test="@office2">
      <xsl:text>&#xA;   Office2: </xsl:text>
      <xsl:value-of select="substring-before(@office2, ' ')" />
      <xsl:text>sq feet</xsl:text>
   </xsl:if>
   <!-- More Customers -->
   <xsl:apply-templates />
</xsl:template>

This assumed that the size of Office1 and Office2 were stored as
attributes on the customer_name element, so it accessed those values
through the paths:

  @office1
  @office2

Now you have them as children of the customer_name element, so you
need to change the paths that tell the processor how to get the
relevant values. The offices are represented by elements called
'office_name' with their 'id' attribute specifying whether they are
'office1' or 'office2'. The size of each office is stored in the
'space' attribute on the 'office_name' element.

Building a location path is all about thinking about where you're
starting from (the 'customer_name' element) and how you can navigate
through the node tree to get to where you want to go.

The first step in your path is to get the 'office_name' children of
'customer_name' (i.e. of the current node). For that you need the
path:

  office_name

To get 'office1' you want the office_name element whose id attribute
is equal to the string 'office1'.  You use a predicate to filter a set
of nodes to get only the ones you want:

  office_name[@id = 'office1']

Now you've got the relevant 'office_name' element, to get its size,
you want that office_name element's 'space' attribute.  That's another
step:

  office_name[@id = 'office1']/@space

Similarly, you can get the relevant value for office2 through:

  office_name[@id = 'office2']/@space

You need to put these location paths into the original template.  You
also need to change the template so that it only applies templates to
its child customer_name elements - if you don't specify what it should
apply templates to, xsl:apply-templates applies templates to *all*
children of the current node, so it'll apply templates to the
office_name elements as well.

So you need to change the template to:

<xsl:template match="customer_name">
   <!-- Customer Name -->
   <xsl:text>Customer Name: </xsl:text><xsl:value-of select="@id" />
   <!-- Revenue -->
   <xsl:text>&#xA;   Revenue: $</xsl:text>
   <xsl:value-of select="@revenue div 100" />
   <!-- Office 1 -->
   <xsl:text>&#xA;   Office1: </xsl:text>
   <xsl:value-of
      select="substring-before(office_name[@id = 'office1']/@space, ' ')" />
   <xsl:text>sq feet</xsl:text>
   <!-- Office 2 -->
   <xsl:if test="office_name[@id = 'office2']">
      <xsl:text>&#xA;   Office2: </xsl:text>
      <xsl:value-of
         select="substring-before(office_name[@id = 'office2']/@space, ' ')" />
      <xsl:text>sq feet</xsl:text>
   </xsl:if>
   <!-- More Customers -->
   <xsl:apply-templates select="customer_name" />
</xsl:template>

If you want to get those details about the offices through applying
templates instead (which is a good idea if you don't know how many
offices there are going to be), then you can apply templates to all
the office_name elements:

<xsl:template match="customer_name">
   <!-- Customer Name -->
   <xsl:text>Customer Name: </xsl:text><xsl:value-of select="@id" />
   <!-- Revenue -->
   <xsl:text>&#xA;   Revenue: $</xsl:text>
   <xsl:value-of select="@revenue div 100" />
   <!-- Offices -->
   <xsl:apply-templates select="office_name" />
   <!-- More Customers -->
   <xsl:apply-templates select="customer_name" />
</xsl:template>

And then create a template for the office_name elements that gives the
relevant output:

<xsl:template match="office_name">
   <xsl:text>&#xA;   </xsl:text>
   <xsl:value-of select="@id" />
   <xsl:text>: </xsl:text>
   <xsl:value-of
      select="substring-before(@space, ' ')" />
   <xsl:text>sq feet</xsl:text>
</xsl:template>

> The reason I am asking is that xerces complained on an attempt to
> apply-template again when the name matches "office_name" saying that
> apply-template can be used only once in one match (namely
> customer_name). Alternate attempt to call-template also did not
> work.

It would be a lot easier to work out what was wrong in your code if
you showed us what it looked like.  Perhaps you've got an
xsl:apply-templates element within another xsl:apply-templates element
or something?

[By the way, your XML as shown still isn't well formed - you've got
attributes in the end tags of the elements.]

I hope that helps,

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]