1
votes

xslt newbie here again. I'll preface this by saying that--like so many who ask questions here--I come from a different programming background, so I don't fully understand loops in xslt and I've probably come up with a contorted method to try to do what I want to do here and am 100% open to learning about other, better methods if this is all horribly misguided.

What I want to do: iterate over a collection of xml files and output csv files. I know how to go one by one and do this, but I want to automate it for a whole batch of files. I tried to figure out working with collections in xslt and gave up after a lot of failed attempts.

Each tei/xml file is named with a unique three-character code: esmpeople.xml, tdspeople.xml, ldbpeople.xml

I have another tei/xml file that lists these codes: codes.xml

So my thought was that I would loop over codes.xml, grab each code, then look for the corresponding xml file, and output a csv file. Here's my code:

codes.xml

<?xml version="1.0"?>
<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml"
    schematypens="http://purl.oclc.org/dsdl/schematron"?><?xml-stylesheet type="text/xsl" href="../xslt/csv-transform-people.xsl"?>
    <TEI xmlns="http://www.tei-c.org/ns/1.0">
    <teiHeader>
        <fileDesc>
            <titleStmt>
                <title/>
            </titleStmt>
            <publicationStmt>
                <p/>
            </publicationStmt>
            <sourceDesc>
<listBibl>
<bibl>
        <note name="workref">OBRAESM</field>
        <note name="workname">Escenas matritenses</field>
        <note name="workauthor">Mesonero Romanos</field>
    </bibl>
    <bibl>
        <note name="workref">OBRATDS</field>
        <note name="workname">Tiempo de silencio</field>
        <note name="workauthor">Luis Martín-Santos</field>
    </bibl>

    <bibl>
        <note name="workref">OBRALDB</field>
        <note name="workname">Luces de Bohemia</field>
        <note name="workauthor">Ramón del Valle-Inclán</field>
    </bibl>
</listBibl>
    </sourceDesc>
        </fileDesc>
        <profileDesc><p/></profileDesc>
    </teiHeader>
    <text>
        <body>
            <p/>
        </body>
    </text>
</TEI>

esmpeople.xml

 <listPerson>
        <person xml:id="PERSSANISIDRO"/>
        <person xml:id="PERSHORACIO"/>
        <person xml:id="PERSBARTOLOMEARGENSOLA"/>
</listPerson>

tdspeople.xml

<listPerson>
        <person xml:id="PERSPEDRO"/>
        <person xml:id="PERSAMADOR"/>
        <person xml:id="PERSRAMONYCAJAL"/>
<listPerson>

ldbpeople.xml

 <listPerson>
        <person xml:id="PERSMAXESTRELLA"/>
        <person xml:id="PERSMADAMACOLLET"/>
        <person xml:id="PERSBUEYAPIS"/>
<listPerson>

csv-transform-people.xsl

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv" xmlns:tei="http://www.tei-c.org/ns/1.0" version="1.0">
        <xsl:output omit-xml-declaration="yes" method="text" encoding="utf-8"/>
        <xsl:variable name="separator" select="','"/>
        <xsl:variable name="newline" select="'&#xA;'"/>


        <xsl:template match="/">

         <xsl:for-each select="//tei:listBibl/tei:bibl">

<!--THIS WAS THE PROBLEM-->
        <!--xsl:variable name="workref" select="//tei:note[@name='workref']"/-->

<!--FIX-->
<xsl:variable name="workref" select="tei:note[@name='workref']"/>

        <xsl:variable name="workreflc" select="lower-case($workref)"/>
        <xsl:variable name="workrefshort" select="replace($workref,'OBRA','')"/>
        <xsl:variable name="workrefshortlc" select="lower-case($workrefshort)"/>

        <xsl:variable name="sourcedocuri" select="concat('xmldb:exist://admin:[email protected]:8080/exist/xmlrpc/db/madrid/xml/',$workrefshortlc,'people.xml')"/>
        <xsl:variable name="sourcedoc" select="doc($sourcedocuri)"/>
        <xsl:variable name="sourcedocperson" select="$sourcedoc//tei:person"/>
            <xsl:result-document href="xmldb:exist://admin:[email protected]:8080/exist/xmlrpc/db/madrid/csv/people2-{$workreflc}.csv">

                <xsl:text>persref</xsl:text>
                <xsl:value-of select="$separator"/>
                 <xsl:text>persworks</xsl:text>
                <xsl:value-of select="$newline"/>
                <xsl:for-each select="$sourcedocperson">
                    <xsl:variable name="people">
                        <xsl:value-of select="@xml:id"/>
                    </xsl:variable>
                    <xsl:value-of select="$people"/>
                    <xsl:value-of select="$separator"/>

                   <xsl:value-of select="$workref"/>
                    <xsl:value-of select="$newline"/>
                </xsl:for-each>

            </xsl:result-document> </xsl:for-each>
        </xsl:template>

</xsl:stylesheet>

Expected result: three csv files saved into the csv directory: people-obraesm.csv, people-obratds.csv, people-obraldb.csv

Result I'm getting: people-obraesm.csv is being created 100% correctly, but then I get this error:

An error occurred: Cannot write more than one result document to the same URI

So I'm able to access all of the xml files and write files into the /csv/ directory without permissions issues. Given the error, it seems to be a problem with the loop in my stylesheet and/or general logic. Really hoping to figure out where I've gone wrong here, because anytime I need to use loops in xslt, I go into deep trial-and-error mode!

ANSWER: I changed the csv-transform-people.xsl above as per Martin's suggestion and it works now!

1

1 Answers

1
votes

Inside of the <xsl:for-each select="//tei:listBibl/tei:bibl">, the context node is the processed //tei:listBibl/tei:bibl, so any selection should be relative to that, meaning, instead of <xsl:variable name="workref" select="//tei:note[@name='workref']"/>, you want <xsl:variable name="workref" select="tei:note[@name='workref']"/>.

On a side note, if using XSLT 2 or 3, it is usually better to use the adequate version (i.e. version="2.0" or version="3.0") in your XSLT and not version="1.0", as that puts the XSLT processor into XPath 1.0 backwards compatibility mode. You would only do that if you move XSLT 1.0 stylesheet previously processed by an XSLT 1 processor to an XSLT 2 or 3 processor.