xmlxsltxslt-1.0marc

xslt working with keys and variables


Hi all this question is related to this: xsl get element values from another node tree But this time I have a working xslt.

I am currently working on the following xml:

 <record>
  <leader>01877nz  a2200433o  4500</leader>
  <controlfield tag="001">1</controlfield>
        ... (more controlfields tag 002 to 010)
  <datafield tag="013" ind1=" " ind2=" ">
   <subfield code="a">formerge</subfield>
  </datafield>
          ... (more datafield tags, datafield tags are from 011 to 999)
  <datafield tag="150" ind1=" " ind2=" ">
   <subfield code="a">Borneo</subfield>
  </datafield>
          ... (more datafield tags, datafield tags are from 011 to 999)
  <datafield tag="550" ind1=" " ind2=" ">
   <subfield code="w">g</subfield>
   <subfield code="a">South East Asia</subfield>
   <subfield code="c">c_7260</subfield>
  </datafield>
       ... (more datafield tags, datafield tags are from 011 to 999)
  </record>

       ... (more records)

  <record>
       ... (more records fields)
       ... (more records fields)
  </record>

  <record>
   <leader>02462nz  a2200553o  4500</leader>
   <controlfield tag="001">2</controlfield>
         ... (more controlfields tag 002 to 010)
   <datafield tag="013" ind1=" " ind2=" ">
    <subfield code="a">formerge</subfield>
   </datafield>
   <datafield tag="035" ind1=" " ind2=" ">
    <subfield code="a">c_7260</subfield>
   </datafield>
       ... (more datafield tags, datafield tags are from 011 to 999)
   <datafield tag="151" ind1=" " ind2=" ">
    <subfield code="a">South East Asia</subfield>
   </datafield>
       ... (more datafield tags, datafield tags are from 011 to 999)
  </record>

I have the following xslt:

<?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim"    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"    xmlns:dc="http://purl.org/dc/elements/1.1/"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="marc">
 <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
 <xsl:key name="term" match="//datafield[@tag='151'][subfield[@code='a']]" use="." />

 <xsl:template match="//datafield[@tag='151'][subfield[@code='a']]">
  <xsl:variable name="t550a" select="//datafield[@tag='550'][subfield[@code='a']]" />
  <xsl:for-each select="key('term','$550a')">
   <xsl:value-of select="//controlfield[@tag='001']" />
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="@*|node()">
  <xsl:copy>
   <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
 </xsl:template>    

</xsl:stylesheet>

With this, I created an index of all 151 through keys (term). I also created a variable 't550a'. Through the keys function, I match the 550a tags (datafield tag="550" subfield code="a") with term key. And then get the value of controlfield tag 001 of that key. I also wanted to get all the nodes of my xml thus the "xsl:copy". With the xslt I have, it seems that datafield tag 151 is being removed.

I wanted to get in 550 field the following:

        ... (other fields omitted)
<datafield tag="550" ind1=" " ind2=" ">
 <subfield code="w">g</subfield>
 <subfield code="a">South East Asia</subfield>
 <subfield code="c">c_7260</subfield>
 <subfield code="0">2</subfield>
</datafield>
          ... (other fields omitted)

<datafield tag="550" ind1=" " ind2=" ">
 <subfield code="w">h</subfield>
 <subfield code="a">Borneo</subfield>
 <subfield code="c">c_1017</subfield>
 <subfield code="0">1</subfield>
</datafield>

So 550 subfield a South East Asia will have additional subfield 0 with value 2, which is based from the controlfield 001 of 151 subfield a South East Asia. And also 550 subfield a Borneo will have additional subfield 0 with value 1, which is based from the controlfield 001 of 151 subfield a Borneo.

Can somebody lead me to doing this correctly. Thanks!


Solution

  • I think you have your template matching the wrong element. If you are looking to add a child node onto the "550" field, you should have a template matching that

    <xsl:template match="datafield[@tag='550'][subfield[@code='a']]">
    

    I would also slightly adjust the key to this, because at the moment the key would be using the whitespace nodes too (although this may be removed if you were using xsl:strip-space

     <xsl:key name="term" match="datafield[@tag='151']" use="subfield[@code='a']" />
    

    The next problem you have is with your definition of the tt50a variable. You really want to be using a relative expression here, because at the moment it will pick up the first matching element anywhere in the document. You should do this instead (which works because you are now positioned on the 550 node)

    <xsl:variable name="t550a" select="subfield[@code='a']" />
    

    However, it appears you are not actually using this variable. You current use of the key, key('term','$550a'), is using a string literal, not the variable. You probably mean to do this:

    <xsl:for-each select="key('term',$t550a)">
    

    And finally, within the xsl:for-each where you doing //controlfield[@tag='001'] then this selects the first controlfield in the document. I think you only want to select the one in the current record. As record is a parent of the current datafield, you can just do this:

    <xsl:value-of select="../controlfield[@tag='001']" />
    

    Try this XSLT

    <xsl:stylesheet version="1.0" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"  xmlns:dc="http://purl.org/dc/elements/1.1/"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="rdf dc marc">
     <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
     <xsl:key name="term" match="datafield[@tag='151']" use="subfield[@code='a']" />
    
     <xsl:template match="datafield[@tag='550'][subfield[@code='a']]">
       <xsl:variable name="t550a" select="subfield[@code='a']" />
       <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
         <xsl:for-each select="key('term',$t550a)">
           <subfield code="0">
             <xsl:value-of select="../controlfield[@tag='001']" />
           </subfield>
          </xsl:for-each>
        </xsl:copy>
     </xsl:template>
    
     <xsl:template match="@*|node()">
      <xsl:copy>
       <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
     </xsl:template>    
    </xsl:stylesheet>
    

    This won't give the output you show in your question exactly, because your input XML only has one 550 node.