Search code examples
xmlxsltxslt-2.0xpath-2.0

XSLT: Pull value from cross-reference


I'm trying to pull values from an aff element that matches a cross-reference id, but not sure what XPath to get specific matched value.

XML:

<article>    
<contrib-group>
    <contrib contrib-type="author">
    <name><surname>Doe</surname><given-names>Jane</given-names></name><email>email here</email><xref ref-type="aff" rid="affa">a</xref><xref ref-type="corresp" rid="cor2">*</xref>
    </contrib>
    <contrib contrib-type="author">
    <name><surname>Done</surname><given-names>John</given-names></name><email>email here</email><xref ref-type="aff" rid="affb">b</xref>
    </contrib>
    <aff id="affa"><label>a</label>Department of Philosophy, <institution>University of XXX</institution>, <country>Germany</country></aff>
    <aff id="affb"><label>b</label>Institute of Logic, <institution>Univeristy of YYY</institution>, Virginia, <country>United States</country></aff>
    </contrib-group>
</article>

XSLT:

<xsl:template match="*">
        <article>
        <xsl:apply-templates select="//contrib-group/contrib"/>
        </article>
    </xsl:template>
    
    <xsl:template match="article/contrib-group/contrib">
        <contrib>
        <surname><xsl:value-of select="name/surname"/></surname>
        <given-names><xsl:value-of select="name/given-names"/></given-names>
        <email><xsl:value-of select="./email"/></email>
        <affiliation>
            <xsl:if test="xref/@rid = following-sibling::aff/@id">
                <xsl:value-of select="following-sibling::aff"/>
            </xsl:if>
        </affiliation>
        </contrib>
    </xsl:template>

I know the <xsl:value-of> is incorrect here - how to select the corresponding linked @id that matches the @rid?

The output gives me both affiliations for both contribs:

<article>
    <contrib>
        <surname>Doe</surname>
        <given-names>Jane</given-names>
        <email>email here</email>
        <affiliation>aDepartment of Philosophy, University of XXX, Germany bInstitute of Logic, Ohio, United States</affiliation>
    </contrib>
    <contrib>
        <surname>Done</surname>
        <given-names>John</given-names>
        <email>Email here</email>
        <affiliation>aDepartment of Philosophy, University of XXX, Germany bInstitute of Logic, Ohio, United States</affiliation>
    </contrib>
</article>

Solution

  • XSLT has a built-in key mechanism for resolving cross-references.

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:key name="aff" match="aff" use="@id" />
    
    <xsl:template match="/article">
        <xsl:copy>
            <xsl:apply-templates select="contrib-group/contrib"/>
        </xsl:copy>
    </xsl:template>
        
    <xsl:template match="contrib">
        <xsl:copy>
            <xsl:copy-of select="name/* | email"/>
            <affiliation>
                <xsl:value-of select="key('aff', xref[@ref-type='aff']/@rid)/(node() except label)" separator=""/>
            </affiliation>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Applied to your input example, this will return:

    Result

    <?xml version="1.0" encoding="UTF-8"?>
    <article>
       <contrib>
          <surname>Doe</surname>
          <given-names>Jane</given-names>
          <email>email here</email>
          <affiliation>Department of Philosophy, University of XXX, Germany</affiliation>
       </contrib>
       <contrib>
          <surname>Done</surname>
          <given-names>John</given-names>
          <email>email here</email>
          <affiliation>Institute of Logic, Univeristy of YYY, Virginia, United States</affiliation>
       </contrib>
    </article>