5
votes

I'm trying to apply a transformation to the below XML using XPath and XSLT.

<AuthorGroup>
  <Author AffiliationID="A1">
    <GivenName>John</GivenName>
    <Initials/>
    <FamilyName>Smith</FamilyName>
    <Degrees/>
    <Roles/>
  </Author>
    <Author AffiliationID="A2">
    <GivenName>Bill</GivenName>
    <Initials/>
    <FamilyName>Atkins</FamilyName>
    <Degrees/>
    <Roles/>
  </Author>
  <Affiliation AFFID="A1">
    <OrgDivision/>
    <OrgName>Some Org</OrgName>
    <OrgAddress/>
  </Affiliation>
  <Affiliation AFFID="A2">
    <OrgDivision/>
    <OrgName>Another Org</OrgName>
    <OrgAddress/>
  </Affiliation>
</AuthorGroup>

I'm trying to combine the author and affiliation names under one node so I'll end up with a structure like:

<AUG>
  <AU><NAME>John Smith</NAME><AUINFO>Some Org</AUINFO></AU>
  <AU><NAME>Bill Atkins</NAME><AUINFO>Another Org</AUINFO></AU>
</AUG>

I'm struggling to find a way of selecting the value of a node who's parent node's attribute matches the value of an attribute of an another node. For example, For the author John Smith I'd like to select the value of the node where the affiliation ID's match.

So far I have some code in my stylesheet like the below but I know the selection inside the node is not working.

<xsl:for-each select="AuthorGroup/Author">
  <AU>
  <NAME><xsl:value-of select="./FamilyName" /> <xsl:value-of select="./GivenName" /></NAME>
  <AUINFO><xsl:value-of select="../Affiliation[@AFFID='NEED TO MATCH AUTHOR AFFILIATIONID']/OrgName" /> </AUINFO>


  </AU>         
</xsl:for-each>

How should I select the value of the correct organisation name for the associated author?

Many thanks

Jim

4
Check my answer using the specific XSLT feature for cross references.user357812

4 Answers

7
votes

As an alternative to using a variable, you can make use of the "current()" operator to get the current Author node you are on

<xsl:value-of select="../Affiliation[@AFFID=current()/@AffiliationID]/OrgName"/>

Thus, if you take the following stylesheet

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:template match="/">
      <AUG>
         <xsl:for-each select="AuthorGroup/Author">
            <AU>
               <NAME>
                  <xsl:value-of select="./FamilyName"/>
                  <xsl:value-of select="./GivenName"/>
               </NAME>
               <AUINFO>
                  <xsl:value-of select="../Affiliation[@AFFID=current()/@AffiliationID]/OrgName"/>
               </AUINFO>
            </AU>
         </xsl:for-each>
      </AUG>
   </xsl:template>
</xsl:stylesheet>

And apply it your given XML, the results should be like so

<AUG>
   <AU>
      <NAME>SmithJohn</NAME>
      <AUINFO>Some Org</AUINFO>
   </AU>
   <AU>
      <NAME>AtkinsBill</NAME>
      <AUINFO>Another Org</AUINFO>
   </AU>
</AUG>
4
votes

Keys are the right tool for cross references. This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:key name="kAffiliationByAFFID" match="Affiliation" use="@AFFID"/>
    <xsl:template match="AuthorGroup">
        <AUG>
            <xsl:apply-templates/>
        </AUG>
    </xsl:template>
    <xsl:template match="Author">
        <AU>
            <NAME>
                <xsl:value-of select="concat(GivenName,' ',FamilyName)"/>
            </NAME>
            <xsl:apply-templates
                 select="key('kAffiliationByAFFID',@AffiliationID)"
                 mode="out"/>
        </AU>
    </xsl:template>
    <xsl:template match="Affiliation"/>
    <xsl:template match="Affiliation" mode="out">
        <AUINFO>
            <xsl:apply-templates/>
        </AUINFO>
    </xsl:template>
</xsl:stylesheet>

Output:

<AUG>
    <AU>
        <NAME>John Smith</NAME>
        <AUINFO>Some Org</AUINFO>
    </AU>
    <AU>
        <NAME>Bill Atkins</NAME>
        <AUINFO>Another Org</AUINFO>
    </AU>
</AUG>
2
votes

Just a quick answer ... hope this helps, can't run it myself for the moment :-S you can put the ID in a variable like:

<xsl:for-each select="AuthorGroup/Author">
  <xsl:variable name="affID" select="@AFFID" />
  <AU>
    <NAME><xsl:value-of select="./FamilyName" /> <xsl:value-of select="./GivenName" /></NAME>
    <AUINFO><xsl:value-of select="../Affiliation[@AFFID=$affID]/OrgName" /> </AUINFO>
  </AU>         
</xsl:for-each>
1
votes
<xsl:for-each select="AuthorGroup/Author">
    <AU>
    <NAME><xsl:value-of select="./FamilyName" /> <xsl:value-of select="./GivenName" />
    </NAME>
     <AUINFO><xsl:value-of select="../Affiliation[@AFFID=./@AffiliationID]/OrgName" /> 
     </AUINFO>