Re: [xsl] getting a label from a schema

Subject: Re: [xsl] getting a label from a schema
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Fri, 20 Apr 2001 09:25:00 +0100
Hi George,

> I am very much an uneducated newbie that I find it difficult even to
> phrase my question, but I cannot find the answer anywhere, so I am
> asking.

OK :)  Just in general, if you're asking about XSLT it makes things a
lot easier if you give a small example of the kind of output that you
want from your input.  I think from your description that given the
following in the XML file:

> My XML file includes:
>          <group type="Fieldx">
>               <item>100.00</item>
>               <item>200.00</item>
>          </group>

and the following in the XML Schema file that's associated with it:

> Each item has a label associated with it by according to its name in
> an .msd schema as in the following:
>    <element name='Income1' type='Fieldx'>
>      <annotation>
>        <appinfo>
>          <xbrl:rollup to='income' weight='0' order='0'/>
>          <xbrl:label xml:lang='en'>Employment income</xbrl:label>
>          <xbrl:label xml:lang='fr'>Revenes d'emploi</xbrl:label>
>        </appinfo>
>      </annotation>
>    </element>

You want two HTML files - one in English:

  Employment income:

and one in French:

  Revenes d'emploi:

or something along those lines?
> 1. How do I get my XSLT stylesheet to make sure that all labels from
> the schema are presented with the item values in an HTML doc, which
> presents the XML data (even those whose value is zero)?

The first thing is that you have to identify the schema file for the
XML document. Now, you should have in your XML document an
xsi:schemaLocation attribute or an xsi:noNamespaceSchemaLocation
attribute that give the URL of your XML Schema. (If you don't have one
of these, then you'll have to pass in the URL for the schema file with
a stylesheet parameter instead.)

It's not clear from your example what namespace the 'group' element is
in, so I'll assume for now that it's in the null namespace (i.e. you
haven't declared a default namespace with an xmlns attribute higher up
the document).  This makes things easier, because the
xsi:noNamespaceSchemaLocation is just a URL that points to the schema
for nodes in the null namespace.

So, to get hold of the schema document, you need to declare the XML
Schema Instance namespace:

<xsl:stylesheet version="1.0"
                exclude-result-prefixes="xsi xbrl">

You can then get the URL of the schema document with the XPath:


and you can retrieve the schema document using the document()

  document(/*/xsi:noNamespaceSchemaLocation, /)

[Note that the second argument there makes sure that the URL is
resolved relative to the source XML that you're processing, rather
than to the stylesheet.]

You can hold this in a variable so that you can access it easily later

<xsl:variable name="schema"
              select="document(/*/xsi:noNamespaceSchemaLocation, /)" />

[One thing I should point out here - the *only* schema information that
you get through this method is the stuff that's actually held in that
primary schema document.  You *don't* get anything that's included or
imported or redefined within the schema.  That requires a lot more
processing (something like the closure() function that Christian
Nentwich has recently proposed would be useful here).  You can expect
that XSLT 2.0 and XPath 2.0 will give a lot more assistance on
retrieving schema information which would also help here.]
Now, when you're processing a group element, then you have to find the
relevant part of the schema document that can give you the labels that
you need. I'm a bit flummoxed by your XML Schema snippet, actually
because it seems as though you're using the type of the group to
retrieve the labels through the *type* of some random *element*. If
the labels are associated with a *type*, then I would have expected
them to be held within the type definition, something like:

<complexType name="Fieldx">
       <xbrl:rollup to='income' weight='0' order='0'/>
       <xbrl:label xml:lang='en'>Employment income</xbrl:label>
       <xbrl:label xml:lang='fr'>Revenes d'emploi</xbrl:label>

After all, there might be lots of elements in your schema that use the
same type - how are you supposed to work out which one contains the
correct labels?

Anyway, it's probably easiest to get to the labels you want using a
key that indexes them according to the type of the element that
they're held within:

<xsl:key name="labels"
         use="ancestor::element/@type" />

With that key in place, then calling:

  key('labels', 'Fieldx')

when the current node is in the schema (such as being the schema
document root not) will give the labels you've defined in the schema.

So, finally, we can construct the template for the group element:

<xsl:template match="group">
   <xsl:variable name="type" select="@type" />
   <!-- this xsl:for-each just changes the current node to the schema
        document element -->
   <xsl:for-each select="$schema">
      <xsl:value-of select="key('labels', $type)" />
      <xsl:for-each select="item">
         <option><xsl:value-of select="." /></option>

[Note that this only gets the *first* label.]

> 2. How do I get my XSLT stylesheet to present the label and item
> values only when the values are not equal to zero?

'When the values are not equal to zero' is ambiguous.  Do you want to
only display those item elements that are not equal to zero?  In that
case, you need:

  <xsl:for-each select="item[. != 0]">

Do you want to only display anything if there are *no* item elements
with a zero value? In that case, you need an xsl:if that tests whether
it's not the case that there is an item child of the group element
that has a zero value:

<xsl:template match="group">
   <xsl:if test="not(item = 0)">

Do you want to display it unless *all* the item elements have a zero
value?  In that case, you need an xsl:if that tests whether there is
any item element without a zero value:

<xsl:template match="group">
   <xsl:if test="item != 0">

> 3. How do I indicate the language desired in my XML document?

Well, first you need to tell the stylesheet which language you want to
use, with a stylesheet parameter.  You define the parameter at the top
level of the stylesheet:

<!-- default value is English -->
<xsl:param name="lang" select="'en'" />

How you pass in the value of the parameter depends on the processor
you're using and how you're using it.  Have a look in your XSLT
processor documentation.

You can then use the $lang parameter to find only those labels whose
language (as indicated by xml:lang) is equal to that $lang parameter.
So when you're picking the label, instead of doing:

  <xsl:value-of select="key('labels', $type)" />


  <xsl:value-of select="key('labels', $type)[lang($lang)]" />

The lang() function returns true for nodes whose language (indicated
through xml:lang) is equal to (or related to) the string passed as the
parameter.  It's better to use that than xml:lang = $lang because
lang() does all sorts of clever stuff to do with resolving inherited
languages and language subsets.

I hope that helps,


Jeni Tennison

 XSL-List info and archive:

Current Thread