2
votes

I have the following XML:

<?xml version="1.0" encoding="UTF-8"?>
 <collection
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.loc.gov/MARC21/slim..."
 xmlns="http://www.loc.gov/MARC21/slim">

 <record>
  <leader>01877nz  a2200433o  4500</leader>
  <controlfield tag="001">1</controlfield>
  <datafield tag="013" ind1=" " ind2=" ">
   <subfield code="a">formerge</subfield>
  </datafield>
          ...
  <datafield tag="150" ind1=" " ind2=" ">
   <subfield code="a">Borneo</subfield>
  </datafield>
          ...
  <datafield tag="550" ind1=" " ind2=" ">
   <subfield code="w">g</subfield>
   <subfield code="a">South East Asia</subfield>
   <subfield code="c">c_7260</subfield>
  </datafield>
       ...
  </record>

       ...

  <record>
       ...
       ...
  </record>

  <record>
   <leader>02462nz  a2200553o  4500</leader>
   <controlfield tag="001">2</controlfield>
         ...
   <datafield tag="013" ind1=" " ind2=" ">
    <subfield code="a">formerge</subfield>
   </datafield>
   <datafield tag="035" ind1=" " ind2=" ">
    <subfield code="a">c_7260</subfield>
   </datafield>
       ...
   <datafield tag="151" ind1=" " ind2=" ">
    <subfield code="a">South East Asia</subfield>
   </datafield>
       ...
  </record>

Starting from datafield tag=550 with child node subfield code a, I want to add the value of controlfield tag 001 based on datafield tag 151 subfield code a. In this case, it's "2". The datafield tag to match to can also be 150 in some cases. There are more on nodes on the the node tree but basically it's like below:

<record>
 <leader>...</leader>
 <controlfield tag="001">..</controlfield> --> this one can be up to 010
 <datafield tag="150" ind1=" " ind2=" "> --> this one can be from 011 to 999
  <subfield code="a">..</subfield> --> attributes can be 0-9, a-z
    ...
  </subfield>
 </datafield>
</record>        

So I still wanted to keep the xml as is just added the value I want to get like below:

      ...
<datafield tag="150" ind1=" " ind2=" ">
 <subfield code="a">Borneo</subfield>
</datafield>
          ...
<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>

Is this even possible with XSLT? Can somebody please lead me to what I need to read up on? I'm thinking "keys" but there could be another one. Thanks in advance!

Update: I have the following templates, I added Abel's code:

<?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:template match="/">
   <xsl:apply-templates />
   <xsl:apply-templates select="//datafield"/>
  </xsl:template>

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

<xsl:variable name="next-ctl" select="
 generate-id(
    ../controlfield[@tag = '001']
    /following-sibling::controlfield[1])" />

<xsl:template match="//datafield[@tag = '550'][subfield[@code = 'a']]">
 <xsl:value-of select="../controlfield[@tag = '001']
  [following-sibling::datafield
    [@tag = '151']
    [subfield[@code = 'a']]
    [following-sibling::controlfield
       [generate-id(.) = $next-ctl
       or not(following-sibling::controlfield)]
    ]
 ]" />
 </xsl:template>

1

1 Answers

0
votes

Is this even possible with XSLT?

Yes, it is meant for tasks like this.

Can somebody please lead me to what I need to read up on?

If you are new to XSLT, this video tutorial by Dimitre Novatchev is a very good introduction on XSLT and will save you a lot of trouble. It's a few dollars or a few hours very well spent.

Starting from datafield tag=550

I.e.:

<xsl:template match="datafield[@tag = '550']">...

... with child node subfield code a,

I.e.:

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

I want to add the value of controlfield tag 001

I.e., assuming focus is from previous matching template:

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

based on datafield tag 151 subfield code a.

Since datafield elements appear to be siblings of controlfield (I assume each controlfield is followed by a set of datafields), I am going to assume they must be amongst the following siblings, but before the next controlfield, but adjust to your needs.

I.e.:

<xsl:variable name="next-ctl" select="
     generate-id(
        ../controlfield[@tag = '001']
        /following-sibling::controlfield[1])" />

<xsl:value-of select="
     ../controlfield[@tag = '001']
     [following-sibling::datafield
        [@tag = '151']
        [subfield[@code = 'a']]
        [following-sibling::controlfield
           [generate-id(.) = $next-ctl
           or not(following-sibling::controlfield)]
        ]
     ]" />

Note: if expressions become complex as these, and simplification is not trivial (i.e. splitting and using variables), you can consider switching to XSLT 2.0, which allows more freedom of expression, comments inside expressions (i.e. the so-called smiley-comments, (:...:)) and has more ways to test (in this case, for instance if one element lies before another one in document order: << and >> operators).

In this case, it's "2".

Above code not tested (your requirements are too complex and I fear I misunderstand them), but it is probably something along these lines.

The datafield tag to match to can also be 150 in some cases.

In which cases you should adjust accordingly.

I'm thinking "keys" but there could be another one.

Yes, this may help, but it depends on the rest of the requirements is this is actually needed (see examples above).