xmlxsltdublin-core

Using XSLT to select after EACH instance in a string/substring


I'm attempting to write an XSLT stylesheet that will handle a Dublin Core (XML) cataloging record and create Chicago, APA, and MLA versions of the citation for each book. I've got everything worked out fine except the APA author one. APA's style for authors requires the author's last name (done), comma, first initial (done), any other initials (my stuck place problem).

What I have right now (and sample DC element is below):

<xsl:value-of select="substring-before(dc:creator[1],',')" /><xsl:text>, </xsl:text><xsl:value-of select="substring(substring-after(dc:creator[1],' '),1,1)" /><xsl:for-each select="dc:creator[position()!=1]"><xsl:choose><xsl:when test="position()=last()"><xsl:text>., &amp; </xsl:text></xsl:when><xsl:otherwise>., </xsl:otherwise></xsl:choose><xsl:value-of select="substring-before(.,',')" /><xsl:text>, </xsl:text><xsl:value-of select="substring(substring-after(.,' '),1,1)" /><xsl:if test="position()=last()">.</xsl:if></xsl:for-each>

For instances of the following format (what out catalog uses):

<dc:creator>Friend, Natasha</dc:creator>

this works just fine and returns: Friend, N.

But for

<dc:creator>Tolkien, J. R. R.</dc:creator>

it returns: Tolkien, J.

In a lot of cases it doesn't matter, but there will be cases where it really needs to return authors' middle initial(s), like J.R.R. Tolkien or J.K. Rowling.

So, I need to be able to return the letter that occurs after each space, not just the first instance of a space.

Any ideas?


Solution

  • I. Here is an easy and natural XSLT 2.0 solution:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/*/*">
      <xsl:value-of separator=", " select=
      "substring-before(., ',')
       , for $n in tokenize(substring-after(., ','), '\s')[.]
            return
              substring($n, 1,1)
      "/>
      <xsl:text>&#xA;</xsl:text>
     </xsl:template>
    </xsl:stylesheet>
    

    When this transformation is applied on the following XML document:

    <t xmlns:dc="some:dc">
     <dc:creator >Friend, Natasha</dc:creator>
    
     <dc:creator>Tolkien, J. R. R.</dc:creator>
    </t>
    

    the wanted, correct result is produced:

    Friend, N
    Tolkien, J., R., R.
    

    II. An XSLT 1.0 solution:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/*/*/text()">
       <xsl:value-of select="substring-before(., ',')"/>
    
       <xsl:call-template name="replaceTokenDelims">
        <xsl:with-param name="pStr" select=
        "concat(normalize-space(substring-after(., ',')), ' ')"/>
        <xsl:with-param name="pToken" select="' '"/>
        <xsl:with-param name="pReplacement" select="', '"/>
       </xsl:call-template>
       <xsl:text>&#xA;</xsl:text>
     </xsl:template>
    
     <xsl:template name="replaceTokenDelims">
        <xsl:param name="pStr"/>
        <xsl:param name="pToken"/>
        <xsl:param name="pReplacement"/>
    
        <xsl:if test="$pStr">
         <xsl:value-of select="$pReplacement"/>
         <xsl:value-of select=
          "substring(substring-before($pStr, $pToken), 1, 1)"/>
    
         <xsl:call-template name="replaceTokenDelims">
          <xsl:with-param name="pStr"
               select="substring-after($pStr, $pToken)"/>
          <xsl:with-param name="pToken" select="$pToken"/>
          <xsl:with-param name="pReplacement" select="$pReplacement"/>
         </xsl:call-template>
        </xsl:if>
     </xsl:template>
    </xsl:stylesheet>
    

    When this transformation is applied on the same XML document (above), again the same correct result is produced:

    Friend, N
    Tolkien, J., R., R.