Embedded XSLT stylesheets for service
authorAdam Dickmeiss <adam@indexdata.dk>
Thu, 27 Oct 2011 11:44:17 +0000 (13:44 +0200)
committerAdam Dickmeiss <adam@indexdata.dk>
Thu, 27 Oct 2011 11:44:17 +0000 (13:44 +0200)
New service definition element, xslt, that allows an embedded stylesheet
to be defined. This can be referred to from pz:xslt as an alternative to
external files.

14 files changed:
NEWS
doc/pazpar2_conf.xml
src/Makefile.am
src/normalize_cache.c
src/normalize_cache.h
src/normalize_record.c
src/normalize_record.h
src/pazpar2_config.c
src/pazpar2_config.h
src/service_xslt.c [new file with mode: 0644]
src/service_xslt.h [new file with mode: 0644]
src/session.c
test/gils_service.xml
win/makefile

diff --git a/NEWS b/NEWS
index 083d60a..794094b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,8 @@
 
+New service definition element, xslt, that allows an embedded stylesheet
+to be defined. This can be referred to from pz:xslt as an alternative to
+external files.
+
 New pz:sortmap:field setting for specifying hints on how to make
 a target natively sort on a field. This is used for command=show in
 conjunction with sort.
index e558c65..dcc1fdf 100644 (file)
        </varlistentry>
 
        <varlistentry>
+       <term id="servicexslt" xreflabel="xslt">xslt</term>
+       <listitem>
+        <para>
+         Defines a XSLT stylesheet. The <literal>xslt</literal>
+         element takes exactly one attribute <literal>id</literal>
+         which names the stylesheet. This can be referred to in target
+         settings <xref linkend="pzxslt"/>.
+        </para>
+        <para>
+         The content of the xslt element is the embedded stylesheet XML
+        </para>
+       </listitem>
+       </varlistentry>
+       <varlistentry>
        <term id="icuchain" xreflabel="icu_chain">icu_chain</term>
        <listitem>
         <para>
     </varlistentry>
 
     <varlistentry>
-     <term>pz:xslt</term>
+     <term id="pzxslt" xreflabel="pz:xslt">pz:xslt</term>
      <listitem>
       <para>
-       Is a comma separated list of of files that specifies
+       Is a comma separated list of of stylesheet names that specifies
        how to convert incoming records to the internal representation.
       </para>
       <para>
+       For each name, the embedded stylesheets (XSL) that comes with the
+       service definition are consulted first and takes precedence over
+       external files; see <xref linkend="servicexslt"/>
+       of service definition).
+       If the name does not match an embedded stylesheet it is
+       considered a filename.
+      </para>
+      <para>
        The suffix of each file specifies the kind of tranformation.
        Suffix "<literal>.xsl</literal>" makes an XSL transform. Suffix
        "<literal>.mmap</literal>" will use the MMAP transform (described below).
index 2f3643d..55d3ed5 100644 (file)
@@ -38,6 +38,7 @@ libpazpar2_a_SOURCES = \
        record.c record.h \
        relevance.c relevance.h \
        sel_thread.c sel_thread.h \
+       service_xslt.c service_xslt.h \
        session.c session.h \
        settings.c settings.h \
        termlists.c termlists.h
index 862a258..21b1f8a 100644 (file)
@@ -56,7 +56,7 @@ normalize_cache_t normalize_cache_create(void)
 }
 
 normalize_record_t normalize_cache_get(normalize_cache_t nc,
-                                       struct conf_config *conf,
+                                       struct conf_service *service,
                                        const char *spec)
 {
     normalize_record_t nt;
@@ -70,7 +70,7 @@ normalize_record_t normalize_cache_get(normalize_cache_t nc,
         nt = ci->nt;
     else
     {
-        nt = normalize_record_create(conf, spec);
+        nt = normalize_record_create(service, spec);
         if (nt)
         {
             ci = nmem_malloc(nc->nmem, sizeof(*ci));
index 46b55ad..fab116e 100644 (file)
@@ -26,7 +26,7 @@ typedef struct normalize_cache_s *normalize_cache_t;
 normalize_cache_t normalize_cache_create(void);
 
 normalize_record_t normalize_cache_get(normalize_cache_t nc,
-                                       struct conf_config *conf,
+                                       struct conf_service *service,
                                        const char *spec);
 void normalize_cache_destroy(normalize_cache_t nc);
 
index e2c28cf..f75a0ee 100644 (file)
@@ -29,14 +29,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include "normalize_record.h"
 
 #include "pazpar2_config.h"
-
+#include "service_xslt.h"
 #include "marcmap.h"
 #include <libxslt/xslt.h>
 #include <libxslt/transform.h>
 
 struct normalize_step {
     struct normalize_step *next;
-    xsltStylesheet *stylesheet;
+    xsltStylesheet *stylesheet;  /* created by normalize_record */
+    xsltStylesheet *stylesheet2; /* external stylesheet (service) */
     struct marcmap *marcmap;
 };
 
@@ -45,7 +46,7 @@ struct normalize_record_s {
     NMEM nmem;
 };
 
-normalize_record_t normalize_record_create(struct conf_config *conf,
+normalize_record_t normalize_record_create(struct conf_service *service,
                                            const char *spec)
 {
     NMEM nmem = nmem_create();
@@ -54,6 +55,7 @@ normalize_record_t normalize_record_create(struct conf_config *conf,
     int i, num;
     int no_errors = 0;
     char **stylesheets;
+    struct conf_config *conf = service->server->config;
 
     nt->nmem = nmem;
 
@@ -65,9 +67,11 @@ normalize_record_t normalize_record_create(struct conf_config *conf,
         *m = nmem_malloc(nt->nmem, sizeof(**m));
         (*m)->marcmap = NULL;
         (*m)->stylesheet = NULL;
-        
-        // XSLT
-        if (!strcmp(&stylesheets[i][strlen(stylesheets[i])-4], ".xsl")) 
+
+        (*m)->stylesheet2 = service_xslt_get(service, stylesheets[i]);
+        if ((*m)->stylesheet2)
+            ;
+        else if (!strcmp(&stylesheets[i][strlen(stylesheets[i])-4], ".xsl")) 
         {    
             if (!((*m)->stylesheet =
                   xsltParseStylesheetFile((xmlChar *) wrbuf_cstr(fname))))
@@ -77,7 +81,6 @@ normalize_record_t normalize_record_create(struct conf_config *conf,
                 no_errors++;
             }
         }
-        // marcmap
         else if (!strcmp(&stylesheets[i][strlen(stylesheets[i])-5], ".mmap"))
         {
             if (!((*m)->marcmap = marcmap_load(wrbuf_cstr(fname), nt->nmem)))
@@ -132,6 +135,8 @@ int normalize_record_transform(normalize_record_t nt, xmlDoc **doc,
            xmlDoc *ndoc;
            if (m->stylesheet)
                ndoc = xsltApplyStylesheet(m->stylesheet, *doc, parms);
+           else if (m->stylesheet2)
+               ndoc = xsltApplyStylesheet(m->stylesheet2, *doc, parms);
            else if (m->marcmap)
                ndoc = marcmap_apply(m->marcmap, *doc);
             else
index 06c953c..25155af 100644 (file)
@@ -21,9 +21,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #define NORMALIZE_RECORD_H
 typedef struct normalize_record_s *normalize_record_t;
 
-struct conf_config;
+struct conf_service;
 
-normalize_record_t normalize_record_create(struct conf_config *conf,
+normalize_record_t normalize_record_create(struct conf_service *service,
                                            const char *spec);
 
 void normalize_record_destroy(normalize_record_t nt);
index c6567bd..dd1c570 100644 (file)
@@ -41,6 +41,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #include "ppmutex.h"
 #include "incref.h"
 #include "pazpar2_config.h"
+#include "service_xslt.h"
 #include "settings.h"
 #include "eventl.h"
 #include "http.h"
@@ -56,6 +57,13 @@ struct conf_config
     database_hosts_t database_hosts;
 };
 
+struct service_xslt
+{
+    char *id;
+    xsltStylesheetPtr xsp;
+    struct service_xslt *next;
+};
+    
 static void conf_metadata_assign(NMEM nmem, 
                                  struct conf_metadata * metadata,
                                  const char *name,
@@ -115,6 +123,7 @@ static struct conf_service *service_init(struct conf_server *server,
     service->nmem = nmem;
     service->next = 0;
     service->databases = 0;
+    service->xslt_list = 0;
     service->server = server;
     service->session_timeout = 60; /* default session timeout */
     service->z3950_session_timeout = 180;
@@ -230,6 +239,7 @@ void service_destroy(struct conf_service *service)
     {
         if (!pazpar2_decref(&service->ref_count, service->mutex))
         {
+            service_xslt_destroy(service);
             pp2_charset_fact_destroy(service->charsets);
             yaz_mutex_destroy(&service->mutex);
             nmem_destroy(service->nmem);
@@ -542,6 +552,11 @@ static struct conf_service *service_create_static(struct conf_server *server,
             if (parse_metadata(service, n, &md_node, &sk_node))
                 return 0;
         }
+        else if (!strcmp((const char *) n->name, (const char *) "xslt"))
+        {
+            if (service_xslt_config(service, n))
+                return 0;
+        }
         else
         {
             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
index f3c346b..403ec16 100644 (file)
@@ -118,6 +118,8 @@ struct conf_service
     /* duplicated from conf_server */
     pp2_charset_fact_t charsets;
 
+    struct service_xslt *xslt_list;
+
     struct database *databases;
     struct conf_server *server;
 };
diff --git a/src/service_xslt.c b/src/service_xslt.c
new file mode 100644 (file)
index 0000000..bd4b580
--- /dev/null
@@ -0,0 +1,123 @@
+/* This file is part of Pazpar2.
+   Copyright (C) 2006-2011 Index Data
+
+Pazpar2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <yaz/yaz-util.h>
+#include <yaz/nmem.h>
+#include <yaz/snprintf.h>
+#include <yaz/tpath.h>
+#include <yaz/xml_include.h>
+
+#include "service_xslt.h"
+#include "pazpar2_config.h"
+
+struct service_xslt
+{
+    char *id;
+    xsltStylesheetPtr xsp;
+    struct service_xslt *next;
+};
+    
+xsltStylesheetPtr service_xslt_get(struct conf_service *service,
+                                   const char *id)
+{
+    struct service_xslt *sx;
+    for (sx = service->xslt_list; sx; sx = sx->next)
+        if (!strcmp(id, sx->id))
+            return sx->xsp;
+    return 0;
+}
+
+void service_xslt_destroy(struct conf_service *service)
+{
+    struct service_xslt *sx = service->xslt_list;
+    for (; sx; sx = sx->next)
+        xsltFreeStylesheet(sx->xsp);
+}
+
+int service_xslt_config(struct conf_service *service, xmlNode *n)
+{
+    xmlDoc *xsp_doc;
+    xmlNode *root = n->children;
+    struct service_xslt *sx;
+    const char *id = 0;
+    struct _xmlAttr *attr;
+    for (attr = n->properties; attr; attr = attr->next)
+        if (!strcmp((const char *) attr->name, "id"))
+            id = (const char *) attr->children->content;
+        else
+        {
+            yaz_log(YLOG_FATAL, "Invalid attribute %s for xslt element",
+                    (const char *) n->name);
+            return -1;
+        }
+    if (!id)
+    {
+        yaz_log(YLOG_FATAL, "Missing attribute id for xslt element");
+        return 0;
+    }
+    while (root && root->type != XML_ELEMENT_NODE)
+        root = root->next;
+    if (!root)
+    {
+        yaz_log(YLOG_FATAL, "Missing content for xslt element");
+        return -1;
+    }
+    for (sx = service->xslt_list; sx; sx = sx->next)
+        if (!strcmp(sx->id, id))
+        {
+            yaz_log(YLOG_FATAL, "Multiple xslt with id=%s", id);
+            return -1;
+        }
+    
+    sx = nmem_malloc(service->nmem, sizeof(*sx));
+    sx->id = nmem_strdup(service->nmem, id);
+    sx->next = service->xslt_list;
+    service->xslt_list = sx;
+    
+    xsp_doc = xmlNewDoc(BAD_CAST "1.0");
+    xmlDocSetRootElement(xsp_doc, xmlCopyNode(root, 1));
+    sx->xsp = xsltParseStylesheetDoc(xsp_doc);
+    if (!sx->xsp)
+    {
+        xmlFreeDoc(xsp_doc);
+        yaz_log(YLOG_FATAL, "Failed to parse XSLT");
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/service_xslt.h b/src/service_xslt.h
new file mode 100644 (file)
index 0000000..fe86cdd
--- /dev/null
@@ -0,0 +1,45 @@
+/* This file is part of Pazpar2.
+   Copyright (C) 2006-2011 Index Data
+
+Pazpar2 is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+#ifndef SERVICE_XSLT_H
+#define SERVICE_XSLT_H
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+
+struct conf_service;
+
+xsltStylesheetPtr service_xslt_get(struct conf_service *service,
+                                   const char *id);
+
+void service_xslt_destroy(struct conf_service *service);
+
+int service_xslt_config(struct conf_service *service, xmlNode *n);
+
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 388e94e..8b76a53 100644 (file)
@@ -438,7 +438,7 @@ static int prepare_map(struct session *se, struct session_database *sdb)
             }
         }
         sdb->map = normalize_cache_get(se->normalize_cache,
-                                       se->service->server->config, s);
+                                       se->service, s);
         if (!sdb->map)
             return -1;
     }
index 71b4d16..f115422 100644 (file)
@@ -18,7 +18,7 @@
 
        <!-- Result normalization settings -->
        <set name="pz:nativesyntax" value="iso2709"/>
-       <set name="pz:xslt" value="marc21_test.xsl"/>
+       <set name="pz:xslt" value="myxslt"/>
        <set name="pz:apdulog" value="1"/>
 
        <set name="pz:maxrecs" value="3" />
       <metadata name="author" brief="yes" termlist="yes" merge="longest" rank="2"/>
       <metadata name="subject" merge="unique" termlist="yes" rank="3"/>
       <metadata name="id"/>
+
+      <xslt id="myxslt">
+<xsl:stylesheet
+    version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:pz="http://www.indexdata.com/pazpar2/1.0"
+    xmlns:marc="http://www.loc.gov/MARC21/slim">
+
+    <xsl:param name="test"/>
+  
+  <xsl:output indent="yes" method="xml" version="1.0" encoding="UTF-8"/>
+
+<!-- Extract metadata from MARC21/USMARC 
+      http://www.loc.gov/marc/bibliographic/ecbdhome.html
+-->  
+  
+  <xsl:template match="marc:record">
+    <xsl:variable name="title_medium" select="marc:datafield[@tag='245']/marc:subfield[@code='h']"/>
+    <xsl:variable name="journal_title" select="marc:datafield[@tag='773']/marc:subfield[@code='t']"/>
+    <xsl:variable name="electronic_location_url" select="marc:datafield[@tag='856']/marc:subfield[@code='u']"/>
+    <xsl:variable name="fulltext_a" select="marc:datafield[@tag='900']/marc:subfield[@code='a']"/>
+    <xsl:variable name="fulltext_b" select="marc:datafield[@tag='900']/marc:subfield[@code='b']"/>
+    <xsl:variable name="medium">
+      <xsl:choose>
+       <xsl:when test="$title_medium">
+         <xsl:value-of select="substring-after(substring-before($title_medium,']'),'[')"/>
+       </xsl:when>
+       <xsl:when test="$fulltext_a">
+         <xsl:text>electronic resource</xsl:text>
+       </xsl:when>
+       <xsl:when test="$fulltext_b">
+         <xsl:text>electronic resource</xsl:text>
+       </xsl:when>
+       <xsl:when test="$electronic_location_url">
+         <xsl:text>electronic resource</xsl:text>
+       </xsl:when>
+       <xsl:when test="$journal_title">
+         <xsl:text>article</xsl:text>
+       </xsl:when>
+       <xsl:otherwise>
+         <xsl:text>book</xsl:text>
+       </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+
+    <pz:record>
+      <xsl:attribute name="mergekey">
+        <xsl:text>title </xsl:text>
+       <xsl:value-of select="marc:datafield[@tag='245']/marc:subfield[@code='a']"/>
+       <xsl:text> author </xsl:text>
+       <xsl:value-of select="marc:datafield[@tag='100']/marc:subfield[@code='a']"/>
+       <xsl:text> medium </xsl:text>
+       <xsl:value-of select="$medium"/>
+      </xsl:attribute>
+
+      <pz:metadata type="test-usersetting-2">
+        test-usersetting-2 data: 
+        <xsl:value-of select="$test"/>
+      </pz:metadata>
+
+      <xsl:for-each select="marc:controlfield[@tag='001']">
+        <pz:metadata type="id">
+          <xsl:value-of select="."/>
+        </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='010']">
+        <pz:metadata type="lccn">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='020']">
+        <pz:metadata type="isbn">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='022']">
+        <pz:metadata type="issn">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='027']">
+        <pz:metadata type="tech-rep-nr">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='100']">
+       <pz:metadata type="author">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+       <pz:metadata type="author-title">
+         <xsl:value-of select="marc:subfield[@code='c']"/>
+       </pz:metadata>
+       <pz:metadata type="author-date">
+         <xsl:value-of select="marc:subfield[@code='d']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='110']">
+       <pz:metadata type="corporate-name">
+           <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+       <pz:metadata type="corporate-location">
+           <xsl:value-of select="marc:subfield[@code='c']"/>
+       </pz:metadata>
+       <pz:metadata type="corporate-date">
+           <xsl:value-of select="marc:subfield[@code='d']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='111']">
+       <pz:metadata type="meeting-name">
+           <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+       <pz:metadata type="meeting-location">
+           <xsl:value-of select="marc:subfield[@code='c']"/>
+       </pz:metadata>
+       <pz:metadata type="meeting-date">
+           <xsl:value-of select="marc:subfield[@code='d']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='260']">
+       <pz:metadata type="date">
+           <xsl:value-of select="marc:subfield[@code='c']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='245']">
+        <pz:metadata type="title">
+          <xsl:value-of select="marc:subfield[@code='a']"/>
+        </pz:metadata>
+        <pz:metadata type="title-remainder">
+          <xsl:value-of select="marc:subfield[@code='b']"/>
+        </pz:metadata>
+        <pz:metadata type="title-responsibility">
+          <xsl:value-of select="marc:subfield[@code='c']"/>
+        </pz:metadata>
+        <pz:metadata type="title-dates">
+          <xsl:value-of select="marc:subfield[@code='f']"/>
+        </pz:metadata>
+        <pz:metadata type="title-medium">
+          <xsl:value-of select="marc:subfield[@code='h']"/>
+        </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='250']">
+       <pz:metadata type="edition">
+           <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='260']">
+        <pz:metadata type="publication-place">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+        <pz:metadata type="publication-name">
+         <xsl:value-of select="marc:subfield[@code='b']"/>
+       </pz:metadata>
+        <pz:metadata type="publication-date">
+         <xsl:value-of select="marc:subfield[@code='c']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='300']">
+       <pz:metadata type="physical-extent">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+       <pz:metadata type="physical-format">
+         <xsl:value-of select="marc:subfield[@code='b']"/>
+       </pz:metadata>
+       <pz:metadata type="physical-dimensions">
+         <xsl:value-of select="marc:subfield[@code='c']"/>
+       </pz:metadata>
+       <pz:metadata type="physical-accomp">
+         <xsl:value-of select="marc:subfield[@code='e']"/>
+       </pz:metadata>
+       <pz:metadata type="physical-unittype">
+         <xsl:value-of select="marc:subfield[@code='f']"/>
+       </pz:metadata>
+       <pz:metadata type="physical-unitsize">
+         <xsl:value-of select="marc:subfield[@code='g']"/>
+       </pz:metadata>
+       <pz:metadata type="physical-specified">
+         <xsl:value-of select="marc:subfield[@code='3']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='440']">
+       <pz:metadata type="series-title">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag &gt;= 500 and @tag &lt;= 599]
+                           [@tag != '506' and @tag != '530' and
+                           @tag != '540' and @tag != '546'
+                            and @tag != '522']">
+        <!-- The tag attribute below will be preserved -->
+       <pz:metadata type="description" tag="{@tag}">
+            <xsl:value-of select="*/text()"/>
+        </pz:metadata>
+      </xsl:for-each>
+      
+      <xsl:for-each select="marc:datafield[@tag='650' or @tag='653']">
+       <pz:metadata type="subject">
+         <xsl:value-of select="marc:subfield[@code='a']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='856']">
+       <pz:metadata type="electronic-url">
+         <xsl:value-of select="marc:subfield[@code='u']"/>
+       </pz:metadata>
+       <pz:metadata type="electronic-text">
+         <xsl:value-of select="marc:subfield[@code='y']"/>
+       </pz:metadata>
+       <pz:metadata type="electronic-note">
+         <xsl:value-of select="marc:subfield[@code='z']"/>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <xsl:for-each select="marc:datafield[@tag='773']">
+       <pz:metadata type="citation">
+         <xsl:for-each select="*">
+           <xsl:value-of select="normalize-space(.)"/>
+           <xsl:text> </xsl:text>
+         </xsl:for-each>
+       </pz:metadata>
+      </xsl:for-each>
+
+      <pz:metadata type="medium">
+       <xsl:value-of select="$medium"/>
+      </pz:metadata>
+      
+      <xsl:if test="$fulltext_a">
+       <pz:metadata type="fulltext">
+         <xsl:value-of select="$fulltext_a"/>
+       </pz:metadata>
+      </xsl:if>
+
+      <xsl:if test="$fulltext_b">
+       <pz:metadata type="fulltext">
+         <xsl:value-of select="$fulltext_b"/>
+       </pz:metadata>
+      </xsl:if>
+    </pz:record>
+  </xsl:template>
+
+</xsl:stylesheet>
+      </xslt>
     </service>
index adf52b4..6fb8912 100644 (file)
@@ -201,6 +201,7 @@ PAZPAR2_OBJS = \
    "$(OBJDIR)\ppmutex.obj" \
    "$(OBJDIR)\incref.obj" \
    "$(OBJDIR)\sel_thread.obj" \
+   "$(OBJDIR)\service_xslt.obj" \
    "$(OBJDIR)\connection.obj"  \
    "$(OBJDIR)\facet_limit.obj"