1

I have an XML that sort of looks like this:

<desc year="1879">
      <date from="1879-08-30" to="1879-08-30"/> 
            <placeName>New York</placeName> 
            <placeName>New Jersey</placeName>
            <note>visiting my grandma</note>
      <date from="1879-10-30" to="1879-11-01"/> 
            <placeName>Berlin</placeName> 
            <note>with my mother</note>
            <placeName>Hamburg</placeName>
</desc>

I want to transfer the desc Elements to event Elements for each placeName. This works out, but either every note is transferred to every event and not just the one from placeName before it or no note elements at all.

This is my current code:

    <xsl:template match="//tei:desc">
     <xsl:variable name="note-content">
                <xsl:for-each select="//tei:placeName">
                    <xsl:if test="following-sibling::tei:note[1]">
                        <xsl:element name="note" namespace="http://www.tei-c.org/ns/1.0">
                            <xsl:value-of select="following-sibling::tei:note[1]"/>
                        </xsl:element>
                    </xsl:if>
                </xsl:for-each>
            </xsl:variable>
<xsl:for-each-group select="*" group-starting-with="tei:date">
      <!-- other specifications -->

      <xsl:for-each select="current-group()[name()='placeName']">
               <xsl:element name="event" namespace="http://www.tei-c.org/ns/1.0">
                 <!-- other specifications -->

                   <xsl:element name="desc" namespace="http://www.tei-c.org/ns/1.0">
                   <xsl:element name="placeName" namespace="http://www.tei-c.org/ns/1.0">
                           <xsl:attribute name="ref"/>
                           <xsl:value-of select="."/>
                   </xsl:element>
                   </xsl:element>
                   <xsl:if test="$note-content !=''">
                       <xsl:element name="note" namespace="http://www.tei-c.org/ns/1.0">
                           <xsl:value-of select="$note-content"/>
                       </xsl:element>
                   </xsl:if>
               </xsl:element>
            </xsl:for-each>
        </xsl:for-each-group>
    </xsl:template>
Laura P
  • 23
  • 4
  • Do I understand you correctly that you want to group the elements within `desc` into `event` wrapper elements? Where each `date` marks the start of a new group (and hence a new `event`)? Perhaps it would be clearer if you edited your question to include another code block showing the TEI which you want your XSLT to produce? – Conal Tuohy Jul 19 '22 at 10:17
  • 1
    You haven't shown desired output, so the only way to try and work out your requirements is to look at your code, which doesn't actually meet your requirements. Also, the indentation of your source XML is very misleading! – Michael Kay Jul 19 '22 at 13:09
  • The main thing wrong here is that you were calculating the `$note-content` only once, by selecting _all_ the `placeName` elements (that's what the `//` at the start of `//tei:placeName` does for you), and then collecting all the notes which are the first `note` elements to follow each of those `placeName` elements (which is _all_ the notes). In my answer I've dealt with the notes inside the `for-each-group`, so only the `note` elements in each group are copied. – Conal Tuohy Jul 19 '22 at 13:34

1 Answers1

0

I'm not 100% sure I know exactly what output you want (it is generally a good idea to show your desired output rather than relying on a description of what you're trying to do), but it looks like you want to group the desc child elements starting with each date, and within that group you want to create a distinct event for every placeName which would then contain the placeName and also the note? Possibly also the date?

Anyway, I hope this suggestion gets you closer to what you want:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:tei="http://www.tei-c.org/ns/1.0"
  xpath-default-namespace="http://www.tei-c.org/ns/1.0">

  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  
    <!-- this template is just to provide a root element for the example outut -->
    <xsl:template match="/">
      <xsl:element name="listEvent" namespace="http://www.tei-c.org/ns/1.0">
        <xsl:apply-templates/>
      </xsl:element>
    </xsl:template>

    <xsl:template match="desc">
      <xsl:for-each-group select="*" group-starting-with="date">
        <xsl:for-each select="current-group()[self::placeName]">
          <xsl:element name="event" namespace="http://www.tei-c.org/ns/1.0">
            <xsl:element name="desc" namespace="http://www.tei-c.org/ns/1.0">
              <!-- copy everything except notes and placeNames -->
              <xsl:copy-of select="current-group()[not(self::note | self::placeName)]"/>
              <!-- copy the current placeName -->
              <xsl:copy-of select="."/>
            </xsl:element>
            <!-- finally copy the note(s) -->
            <xsl:copy-of select="current-group()[self::note]"/>
          </xsl:element>
        </xsl:for-each>
      </xsl:for-each-group>
    </xsl:template>
    
</xsl:stylesheet>

Result:

<listEvent xmlns="http://www.tei-c.org/ns/1.0">
   <event>
      <desc>
         <date from="1879-08-30" to="1879-08-30"/>
         <placeName>New York</placeName>
      </desc>
      <note>visiting my grandma</note>
   </event>
   <event>
      <desc>
         <date from="1879-08-30" to="1879-08-30"/>
         <placeName>New Jersey</placeName>
      </desc>
      <note>visiting my grandma</note>
   </event>
   <event>
      <desc>
         <date from="1879-10-30" to="1879-11-01"/>
         <placeName>Berlin</placeName>
      </desc>
      <note>with my mother</note>
   </event>
   <event>
      <desc>
         <date from="1879-10-30" to="1879-11-01"/>
         <placeName>Hamburg</placeName>
      </desc>
      <note>with my mother</note>
   </event>
</listEvent>
Conal Tuohy
  • 2,561
  • 1
  • 8
  • 15
  • Also, rather than copying the `date` elements, you might want to just copy their attributes onto the `event` elements, which you could do by putting `` after the `` line. – Conal Tuohy Jul 19 '22 at 13:40