Re: [xsl] constructing an element with an element's content based on the content of sibling element

Subject: Re: [xsl] constructing an element with an element's content based on the content of sibling element
From: Syd Bauman <Syd_Bauman@xxxxxxxxx>
Date: Mon, 11 Jun 2012 03:05:46 -0400
You're welcome. 

I'm beginning to suspect that, like many folks who come to XSLT from
procedural languages like Perl or C, you are using mostly "pull"
processing (get X, put it here; get Y, put it next ...) rather than
mostly "push" processing (whenever you hit an X, do this; whenever
you hit a Y, do that).

The code I sent presumed that your XSLT was busy processing the
*parent* of the <productidentifier> element (e.g. a <containeditem>,
<notforsale>, <product>, <relatedproduct>, or <set>) when you wanted
the ISBN spit out as a <controlfield>. If you're going to use 
      <xsl:for-each select="productidentifier">
        <xsl:call-template name="isbn"/>
      </xsl:for-each>
then in the "isbn" template you're calling, the context node will be
the <productidentifier>. Thus to test for the existence of a <b221>
child element with the value '15', you test any one of the following,
which are equivalent
   self::productidentifier/child::b221 = '15'
   self::productidentifier/b221 = '15'
   ./child::b221 = '15'
   ./b221 = '15'
   b221 = '15'

So in the code you just posted, 
> <xsl:template name="isbn" match="productidentifier">
>   <xsl:choose>
>    <xsl:when test="productidentifier/b221 = '15'">
>      <xsl:apply-templates select="productidentifier[b221='15']"/>

The template is being fired with a <productidentifier> as the current
node. So the test "productidentifier/b221" is asking to look at each
<b221> child of the <productidentifier> child of the currently being
processed <productidentifier>. Since there are no <productidentifier>
elements as children of the current <productidentifier>, the test can
never be true.

So you might try something like

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:marc="http://www.loc.gov/MARC21/slim"; version="2.0">

  <xsl:template match="/">
    <marc:pretend-wrapper-to-hold-multiple-records>
      <xsl:apply-templates select="wrapper/product"/>
    </marc:pretend-wrapper-to-hold-multiple-records>
  </xsl:template>

  <xsl:template match="product">
    <marc:record>
      <xsl:for-each select="productidentifier">
        <xsl:call-template name="isbn"/>
      </xsl:for-each>
    </marc:record>
  </xsl:template>

  <xsl:template name="isbn" match="productidentifier">
    <xsl:choose>
      <!-- first take care of "I am ISBN 13" case -->
      <xsl:when test="child::b221 = '15'">
        <controlfield tag="001">
          <xsl:text>ISBN13 = </xsl:text>
          <xsl:value-of select="b244"/>
        </controlfield>
      </xsl:when>
      <!-- then ignore case where I have a sibling that is ISBN 13 -->
      <xsl:when test="parent::*/child::productidentifier/child::b221 = '15'"/>
      <!-- no sibling ISBN 13, so use ISBN 10, if that's what I am -->
      <xsl:when test="child::b221 = '02'">
        <controlfield tag="001">
          <xsl:text>ISBN10 = </xsl:text>
          <xsl:value-of select="b244"/>
        </controlfield>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>Danger, Will Robinson! a 'productidentifier' that is confusing</xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Although I personally prefer the previous version.


> Thanks very much for your reply, Syd.
> 
> My apologies, I didn't search the XSL archive specifically, many of
> the results from my google search queries included the archive. I
> also poured over Kay's tomb for the functions not(), exists(),
> empty() (got really excited for a moment with element
> -available()).
> 
> My stylesheet has nearly 1300 lines of code transforming ONIX for
> Books 2.1 into MARCXML. I'm using XSLT 2.0 (use tokenize() not
> available in 1.0); Stylus Studio Enterprise edition 2010.
> 
> In fact, the scenario for the need of an alternate ISBN is small
> but the variability of publisher encoding warrants this test I
> think.
> 
> In place of the stylesheet segment in my OP, I substituted a
> call-template based on your reply. The main template of my
> stylesheet matches the product element with ONIXMessage as the
> context and I use call-template for the construction of 1XX, 7XX
> and 245 MARC fields given the complexity of those fields with
> respect to name and title elements in ONIX as well as punctuation
> in the MARCXML fields.
> 
> Hadn't thought to use call-template here but also hadn't considered
> the predicates you offered, so I tried this based on your
> suggestion but get no controlfields at all. Wan't sure about the
> context of the value-of select paths, so tried value-of
> select="following-sibling::b244", too.
> 
> <marc:record>
> .
> .
> <xsl:for-each select="productidentifier">
>   <xsl:call-template name="isbn"/>
> </xsl:for-each>
> .
> .
> </marc:record
> 
> <xsl:template name="isbn" match="productidentifier">
>   <xsl:choose>
>    <xsl:when test="productidentifier/b221 = '15'">
>      <xsl:apply-templates select="productidentifier[b221='15']"/>
>     </xsl:when>
>     <xsl:otherwise>
>     <xsl:apply-templates select="productidentifier[b221='02']"/>
>     </xsl:otherwise>
>   </xsl:choose>
> </xsl:template>
> 
> <xsl:template match="productidentifier[b221 eq '02']">
>   <controlfield tag="001">
>     <xsl:text>xyz</xsl:text>
>     <xsl:value-of select="b244"/>
>   </controlfield>
> </xsl:template>
> 
> <xsl:template match="productidentifier[b221 eq '15']">
>   <controlfield tag="001">
>    <xsl:text>xyz</xsl:text>
>    <xsl:value-of select="b244"/>
>   </controlfield>
> </xsl:template>
> 
> Most of my earlier efforts centered on the use of
> xsl:choose and xsl:otherwise.  All failed so think I'm
> lacking a basic understanding of an important XSLT
> concept.
> 
> Tried attaching a file with 3 records with the variation of presence
> of different product identifiers but my message was rejected even
> though in a text file.

Current Thread