Re: [xsl] Check for null value and Check if child tag exists

Subject: Re: [xsl] Check for null value and Check if child tag exists
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Sun, 15 Apr 2001 15:16:49 +0100
Hi Priya,

> I'm working on transfering data from and XML file to Oracle
> Database. For this I need to convert my XML File into the canonical
> form with the help of an XSLT. These are a few problems am facing
>
> 1) I need to be able to check if a particular tag has one or more
> child tags and be able to get their values.

You can check whether a particular element has a particular child
element by trying to select the child element and testing the
resulting node set. If the node set has a node in it, then it shows
that the child element exists. If the node set doesn't have a node
element, then it doesn't. Empty node sets evaluate as boolean false,
while ones with nodes in evaluate as boolean true.

> 2) I also need to be able to finout if a tag has a null value.

By 'null value' you probably mean 'empty' - doesn't have any child
nodes - no text, no elements.  As above, you can test this by trying
to create a node set of the child nodes that show whether it's a
'null' value or not.  So if it's any nodes, then you could use:

  node()

If you're only interested in text and element content (i.e. an element
will still have a 'null' value if it holds a comment or a processing
instruction), then you could use:

  *|text()

Or if you're just concerned about the text content, and you don't care
about whitespace, then you could use:

  normalize-space()

This gets the string value of the context node and strips any leading
or trailing whitespace, so it only returns true if there is some
character data within the element.

Anyway, that's just to answer your questions - as far as doing what
you're trying to do is concerned, you are trying to get a row element
for every City element in your input XML.  If the City element has a
parent Region element, then you want to use the @Name of that Region
in the row, and the Country/@Name is used as well.

The rowset element in the result corresponds to the World element in
your source, so I'd have a World-matching template to create it. I'd
apply templates to those City elements directly within that, to save
the processor some work in trying to apply templates to everything:

<xsl:template match="World">
   <rowset>
      <xsl:apply-templates select="Country/City |
                                   Country/Region/City" />
   </rowset>
</xsl:template>

[Note: you could use .//City instead, but the above is a little more
efficient because it avoids the descendant axis.]

Now, for each City element that's having templates applied to it, you
want to create a row element in the result.  So again, I'd make a
template matching the City elements.  In this template, you need to
get the value of the City's Region (if it has one) and its Country.
You can get these by moving up the node tree, using the ancestor::
axis:

<xsl:template match="City">
   <row>
      <country>
         <xsl:value-of select="ancestor::Country/@Name" />
      </country>
      <region>
         <xsl:value-of select="parent::Region/@Name" />
      </region>
      <city>
         <xsl:value-of select="@Name" />
      </city>
   </row>
</xsl:template>

The one difference between the sample output that you gave and that
given by the two templates above is that in your source you have:

  <Country Name="Afghanistan">
     <Region Name="Herat">
        <City Name="Herat" Time="GMT+04:30"></City>
     </Region>
     ...
  </Country>

Giving:

  <row>
     <country>Afghanistan">
     <region />
     <city>Herat</city>
  </row>

Whereas the above templates have Herat as the region as well as the
city.  I guess that you don't want to have the region specified if
its Name is the same as the Name of the City?  In that case, you need
to change the above template to contain, the following, which tests
whether the region is called the same as the city and only adds it if
not:

  <region>
     <xsl:variable name="region" select="parent::Region/@Name" />
     <xsl:if test="$region != @Name">
        <xsl:value-of select="$region" />
     </xsl:if>
  </region>

I hope that helps,

Jeni

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



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


Current Thread