OnceUponATime
OnceUponATime

Reputation: 488

Add hyperlinks for different <place> tags in XML to HTML transformation with XSLT

I have a set of XML files where place names (in divergent historic spelling) are tagged as <place>: cf. sample file. The place tag also contains the attribute link, whose value is a hyperlink to a World Historical Gazetteer page, e.g.:

<place name="Wien" type="place_of_issue"
      link="http://whgazetteer.org/places/12346175/portal">Wien</place>

Converting the XML files to HTML with XSLT, I want every such tag in the text to be replaced by a hyperlink <a href>, linking to the same WHG URL.

A minimal version of my XSL based on Michael Hor-257k's answer is:

   <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"/>
    <xsl:template match="/">
        <!-- Jekyll statement --> 
        --- 
        <xsl:for-each select="source/metadata/digital_surrogate">
            layout: page 
            title: <xsl:value-of select="@filename"/>
            permalink: /<xsl:value-of select="@filename"/>/
            exclude: true 
            ---
        </xsl:for-each>
        <!-- Get transcription with links --> 
        <div class="flex-container">
                   
            <div><p><strong>Transkription:</strong></p>
                <p><xsl:apply-templates select="//div">
                </xsl:apply-templates>
 <!-- include WHG links -->
                    <xsl:for-each select="//place"> 
                    <p>Genannte Orte im World Historical Gazetteer:</p>
                    <a href="{//place/@link}" target="_blank">
                        <xsl:value-of select="."/>
                    </a><br/>
                    </xsl:for-each>
                </p><br/>
            </div>
        </div>
    </xsl:template>
</xsl:stylesheet>

This at least correctly displays all place names mentioned in the correct order, with correct WHG links and correct names:

<a href="http://whgazetteer.org/places/12346175/portal" target="_blank">Wien</a>
<a href="http://whgazetteer.org/places/13067462/portal" target="_blank">Maintz</a>
<a href="http://whgazetteer.org/places/12346175/portal" target="_blank">Wien</a>

However, the links still appear below the transcription, not within.

My desired HTML output would be:

<div class="flex-container">
   <div>
      <p><strong>Transkription:</strong></p>
      <p>
              Wir Vorsteher und gesamte
         Meister des ehrsamen Handwerks der b&uuml;rgerl:[ichen] Tischlern in der K:[aiserlich]
              K:[&ouml;niglichen] Haubt = und Residenz Stadt <a href="http://whgazetteer.org/places/12346175/portal" target="_blank">Wien</a> (beglaubigen) hiermit,
              da&szlig; gegenwertiger Tischlergesell, Namens Georg
              Gramer von <a href="http://whgazetteer.org/places/13067462/portal" target="_blank">Maintz</a> - -
[etc.]
</p>
</div>
</div>

Upvotes: 0

Views: 482

Answers (3)

OnceUponATime
OnceUponATime

Reputation: 488

The two previous answers helped me understand the nesting rules in XSL that I wasn't familiar with.

So here is the full XSL that does exactly what I want:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"/>

    <!-- BESCHREIBUNG, BILD UND TEXT AUF "SOURCE" EBENE -->
    <xsl:template match="source">
        <!-- Jekyll statement --> --- <xsl:for-each select="metadata/digital_surrogate">
            layout: page title: <xsl:value-of select="@filename"/> permalink: /<xsl:value-of
                select="@filename"/>/ exclude: true --- </xsl:for-each>
        <!-- Metadaten -->
        <xsl:for-each select="metadata/source_description">
            <h3>Ausstellungsort: <xsl:value-of select="@place"/></h3>
            <h3>Ausstellungsdatum: <xsl:value-of select="@date"/></h3>
        </xsl:for-each>
        <xsl:for-each select="edition">
            <h4 align="center">ediert von: <xsl:value-of select="editors/@name"/></h4>
            <h4 align="center">zuletzt bearbeitet: <xsl:value-of
                    select="transcription_info/@last_revision_date"/></h4>
        </xsl:for-each>
        <br/>
        <xsl:for-each select="metadata/digital_surrogate">
            <xsl:variable name="urlVar">
                <xsl:value-of select="@URL"/>
            </xsl:variable>
            <img src="{$urlVar}" valign="top"/>
        </xsl:for-each>
    <!-- HTML-Flex-Container für Digitalisat und Inhalt -->
        <div class="flex-container">
            <div>
                <p>
                    <strong>Graphische Elemente:</strong>
                </p>
                <p>
                    Art des Siegels: <xsl:value-of select="metadata/source_description/@seal"/>
                    Sonstiges: <xsl:for-each select="visual_element"/>
                </p>
                <br/>
            </div>
        </div>
        <hr/>
        <div class="flex-container">
            <div>
                <p>
                    <strong>Transkription:</strong>
                </p>
                <p>
                   <xsl:apply-templates select="content/body/div"/>
                </p>
            </div>
            <div>
                <p>
                    <strong>Übersetzung:</strong>
                </p>
                <p>
                    <xsl:apply-templates select="translation"/>
                </p>
                <br/>
            </div>
        </div>
</xsl:template>
    
    <!-- WHG LINKS AUF EBENE DIV UND TEXT -->
    <xsl:template match="div">
        <p>
            <xsl:apply-templates select="text"/>
        </p>
    </xsl:template>
    
    <xsl:template match="place">
        <a href="{@link}" target="_blank">
            <xsl:value-of select="."/>
        </a>    
    </xsl:template>
</xsl:stylesheet>

Upvotes: 0

michael.hor257k
michael.hor257k

Reputation: 117175

I am guessing that instead of:

                <xsl:variable name="urlPlace">
                    <xsl:value-of select="@link"/>
                </xsl:variable>
                <a href="{$urlPlace}" target="_blank"><xsl:apply-templates select="//place"/></a>

you want to do:

                <a href="{@link}" target="_blank">
                    <xsl:value-of select="."/>
                </a>

Untested, because no example to test with was provided.


--- added ---

See if you can use this as your starting point:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>

<xsl:template match="/source">
    <html>
        <body>
            <xsl:apply-templates select="content/body/div"/>
        </body>
    </html>
</xsl:template>

<xsl:template match="div">
    <p>
        <xsl:apply-templates select="text"/>
    </p>
</xsl:template>

<xsl:template match="place">
    <a href="{@link}" target="_blank">
        <xsl:value-of select="."/>
    </a>    
</xsl:template>

</xsl:stylesheet>

To understand this and adapt it to your needs, you will need to study XSLT's processing model: https://www.w3.org/TR/1999/REC-xslt-19991116/#section-Processing-Model

Upvotes: 2

Martin Honnen
Martin Honnen

Reputation: 167726

If you want to transform a place element like <place name="Wien" type="place_of_issue" link="http://whgazetteer.org/places/12346175/portal">Wien</place> into an HTML a element use a template

<xsl:template match="place">
  <a href="{@link}">
    <xsl:value-of select="."/>
  </a>
</xsl:template>

Write templates for the transformation of other elements like the ancestors of the places and make sure these templates process child nodes with <xsl:apply-templates/>.

That way the structure and order of the input should be preserved, only that you have HTML in the output reflecting the semantic XML elements you have in the input.

Upvotes: 1

Related Questions