Grouping *almost* unique ids - xslt

I'm struggling with grouping ids that are almost the same. Basically I need to group the content of the PF.A1PF and PersIntr.Personnel.AlPersonnelActive sections and strip the substring before to get my id. I do not have a listing of all the section ids.
Please see below for examples.
Here's the current XML:
<group>
<section id="unique_1_Connect_42_PF.AlPF">
<msgph id="doc" xml:lang="en_US">It has been a external net failure. The pumps are
blocked.</msgph>
<msgph id="cstext" xml:lang="en_US">Mains error</msgph>
<msgph id="localtext" xml:lang="en_US">Mains error</msgph>
</section>
<section id="unique_1_Connect_42_PersIntr.Personnel.AlPersonnelActive">
<msgph id="doc" xml:lang="en_US">Personal alarm warning time has run out without reset.
Personnel in danger !</msgph>
<msgph id="cstext" xml:lang="en_US">Personal alarm</msgph>
<msgph id="localtext" xml:lang="en_US">Pers. alarm</msgph>
</section>
<section id="unique_2_Connect_42_PF.AlPF">
<msgph id="doc" xml:lang="es_ES">Ha habido un fallo de red externa. Las bombas están
bloquedas.</msgph>
<msgph id="cstext" xml:lang="es_ES">Fallo energía de entrada</msgph>
<msgph id="localtext" xml:lang="es_ES">Fallo energía</msgph>
</section>
<section id="unique_2_Connect_42_PersIntr.Personnel.AlPersonnelActive">
<msgph id="doc" xml:lang="es_ES">Tiempo de espera de la alarma de personal ha finalizado sin
reseteo. ¡Personal en peligro!</msgph>
<msgph id="cstext" xml:lang="es_ES">Alarma personal</msgph>
<msgph id="localtext" xml:lang="es_ES">Alarma personal</msgph>
</section>
Here's what I need to output:
<Rsc Id="PF.AlPF">
<Documentation en_US="It has been a external net failure. The pumps are blocked."
es_ES="Ha habido un fallo de red externa. Las bombas están bloquedas."/>
<CSText en_US="Mains error" es_ES="Fallo energía de entrada"/>
<LocalText en_US="Mains error" es_ES="Fallo energía"/>
</Rsc>
<Rsc Id="PersIntr.Personnel.AlPersonnelActive">
<Documentation
en_US="Personal alarm warning time has run out without reset. Personnel in danger !"
es_ES="Tiempo de espera de la alarma de personal ha finalizado sin reseteo. ¡Personal en peligro!"/>
<CSText en_US="Personal alarm" es_ES="Alarma personal"/>
<LocalText en_US="Pers. alarm" es_ES="Alarma personal"/>
</Rsc>
I really appreciate any insight. Thanks in advance for your attention.
Kind regards,
Anne
Update:
Thanks so much, #Dimitre, #Michael. I really appreciate your help with this challenge. I had a little trouble with the element resolving in this portion of the 2.0 solution:
<xsl:element name="{$vNewNames[starts-with(lower-case(.),current()/#id)]}">
<xsl:for-each select="current-group()">
<xsl:attribute name="{#xml:lang}" select="."/>
</xsl:for-each>
</xsl:element>
Here's what finally worked:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<Resources>
<Type key="Alarm">
<xsl:for-each-group select="//section" group-by="substring-after(#id, '_42_')">
<xsl:variable name="currentID" select="substring-after(#id, '_42_')"/>
<xsl:element name="Rsc">
<xsl:attribute name="id" select="$currentID"/>
<xsl:for-each-group select="//section[$currentID]/msgph"
group-by="substring-after(#id, '_42_')">
<xsl:choose>
<xsl:when test="substring-after(#id, '_42_')='doc'">
<Documentation>
<xsl:for-each select="current-group()">
<xsl:if test="contains(parent::*/#id,$currentID)">
<xsl:attribute name="{#xml:lang}" select="."/>
</xsl:if>
</xsl:for-each>
</Documentation>
</xsl:when>
<xsl:when test="substring-after(#id, '_42_')='cstext'">
<CSText>
<xsl:for-each select="current-group()">
<xsl:if test="contains(parent::*/#id,$currentID)">
<xsl:attribute name="{#xml:lang}" select="."/>
</xsl:if>
</xsl:for-each>
</CSText>
</xsl:when>
<xsl:when test="substring-after(#id, '_42_')='localtext'">
<LocalText>
<xsl:for-each select="current-group()">
<xsl:if test="contains(parent::*/#id,$currentID)">
<xsl:attribute name="{#xml:lang}" select="."/>
</xsl:if>
</xsl:for-each>
</LocalText>
</xsl:when>
</xsl:choose>
</xsl:for-each-group>
</xsl:element>
</xsl:for-each-group>
</Type>
</Resources>
</xsl:template>
Thanks again!
Anne

This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="ksectById" match="section"
use="substring-after(#id, '42_')"/>
<xsl:template match=
"section[generate-id()
=
generate-id(key('ksectById',
substring-after(#id, '42_')
)[1]
)
]
">
<xsl:variable name="vId" select=
"substring-after(#id, '42_')"/>
<xsl:variable name="vGroup" select=
"key('ksectById',$vId)"/>
<Rsc Id="{$vId}">
<Documentation>
<xsl:apply-templates select=
"$vGroup/msgph[#id='doc']/#xml:lang"/>
</Documentation>
<CSText>
<xsl:apply-templates select=
"$vGroup/msgph[#id='cstext']/#xml:lang"/>
</CSText>
<LocalText>
<xsl:apply-templates select=
"$vGroup/msgph[#id='localtext']/#xml:lang"/>
</LocalText>
</Rsc>
</xsl:template>
<xsl:template match="#xml:lang">
<xsl:attribute name="{.}">
<xsl:value-of select=".."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*/*" priority="-1"/>
</xsl:stylesheet>
when applied on the provided XML document:
<group>
<section id="unique_1_Connect_42_PF.AlPF">
<msgph id="doc" xml:lang="en_US">It has been a external net failure. The pumps are blocked.</msgph>
<msgph id="cstext" xml:lang="en_US">Mains error</msgph>
<msgph id="localtext" xml:lang="en_US">Mains error</msgph>
</section>
<section id="unique_1_Connect_42_PersIntr.Personnel.AlPersonnelActive">
<msgph id="doc" xml:lang="en_US">Personal alarm warning time has run out without reset. Personnel in danger !</msgph>
<msgph id="cstext" xml:lang="en_US">Personal alarm</msgph>
<msgph id="localtext" xml:lang="en_US">Pers. alarm</msgph>
</section>
<section id="unique_2_Connect_42_PF.AlPF">
<msgph id="doc" xml:lang="es_ES">Ha habido un fallo de red externa. Las bombas están bloquedas.</msgph>
<msgph id="cstext" xml:lang="es_ES">Fallo energía de entrada</msgph>
<msgph id="localtext" xml:lang="es_ES">Fallo energía</msgph>
</section>
<section id="unique_2_Connect_42_PersIntr.Personnel.AlPersonnelActive">
<msgph id="doc" xml:lang="es_ES">Tiempo de espera de la alarma de personal ha finalizado sin reseteo. ¡Personal en peligro!</msgph>
<msgph id="cstext" xml:lang="es_ES">Alarma personal</msgph>
<msgph id="localtext" xml:lang="es_ES">Alarma personal</msgph>
</section>
</group>
produces exactly the wanted, correct result:
<Rsc Id="PF.AlPF">
<Documentation en_US="It has been a external net failure. The pumps are blocked." es_ES="Ha habido un fallo de red externa. Las bombas están bloquedas."/>
<CSText en_US="Mains error" es_ES="Fallo energía de entrada"/>
<LocalText en_US="Mains error" es_ES="Fallo energía"/>
</Rsc>
<Rsc Id="PersIntr.Personnel.AlPersonnelActive">
<Documentation en_US="Personal alarm warning time has run out without reset. Personnel in danger !" es_ES="Tiempo de espera de la alarma de personal ha finalizado sin reseteo. ¡Personal en peligro!"/>
<CSText en_US="Personal alarm" es_ES="Alarma personal"/>
<LocalText en_US="Pers. alarm" es_ES="Alarma personal"/>
</Rsc>
Explanation: Muenchian grouping with key that is defined as a substring of the id attribute.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vNewNames" select=
"'Documentation', 'CSText', 'LocalText'"/>
<xsl:template match="/*">
<xsl:for-each-group select="section"
group-by="substring-after(#id, '_42_')">
<xsl:variable name="vId" select=
"substring-after(#id, '42_')"/>
<Rsc Id="{$vId}">
<xsl:for-each-group select="current-group()/*"
group-by="#id">
<xsl:element name=
"{$vNewNames[starts-with(lower-case(.),current()/#id)]}">
<xsl:for-each select="current-group()">
<xsl:attribute name="{#xml:lang}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</Rsc>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
This code uses some very powerful and most natural XSLT 2.0 features, such as <xsl:for-eac-group>, current-group(), sequences, the select attribute of <xsl:attribute>. The result is almost twice shorter code that is much more readable and maintainable.

I don't know a considerable amount about XSLT, but it seems like you should be able to use the XSLT substring function to strip out the first 22 characters of each section[id] and return the significant portion.

Define "almost". Only then you'll be able to come up with a reasonable solution. Most likely you will then solve it yourself with basic xslt functions.

Related

XSLT 3 multi-step transformation

Building from the transformation in this post, I'm now trying to integrate it into a two step transformation where the same node is transformed twice. Tested independent of each other, the transformations work. For reasons I don't understand, when I bring them together using modes, it's not going through the steps correctly - somehow the modes and variables are not aligned correctly? Fiddle here.
Given this XML:
<TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="MS609-1577">
<teiHeader/>
<text>
<body>
<ab xml:id="MS609-1577-LA" xml:lang="la">
<seg type="dep_event" subtype="sighting" xml:id="MS609-1214-1"><pb break="y" n="80r"/><lb break="y" n="1"/>Item. <date type="deposition_date" when="1245-07-11" xml:id="MS609-1214_depdate">Anno Domini M°CC°XL°V° II° Ydus Junii</date>.
<persName ref="#peire_de_saint-michel" role="dep">P<supplied reason="abbr-name">etrus</supplied> de Sancto Michaele, miles</persName>, testis juratus dixit quod vidit apud
<placeName ref="#laurac_aude" type="sighting_loc">Laurac
<persName ref="#heretics_in_public" role="her">hereticos</persName><lb break="y" n="2"/>publice manentes</placeName>
set nullam familiari<del type="expunctus" rend="after">a</del>tatem habuit cum eis. <date type="sighting_date" when="1225" datingPoint="#MS609-1214_depdate" unit="y" interval="-20">Et sunt XX anni vel circa</date>.</seg>
</ab>
</body>
</text>
</TEI>
My objective is to transform this fragment:
<date type="deposition_date" when="1245-07-11" xml:id="MS609-1214_depdate">Anno Domini M°CC°XL°V° II° Ydus Junii</date>.
Into this ('moving' some text and applying analyze-string to the same node) :
<date type="deposition_date" when="1245-07-11" xml:id="MS609-1214_depdate">Anno Domini M<hi rend="sup">o</hi>CC<hi rend="sup">o</hi>XL<hi rend="sup">o</hi>V<hi rend="sup">o</hi> II<hi rend="sup">o</hi> Ydus Junii.</date>
And the rest copy without changes.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tei="http://www.tei-c.org/ns/1.0"
exclude-result-prefixes="tei"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="no"/>
<xsl:template match="/">
<xsl:variable name="step-one-result">
<xsl:apply-templates select="/" mode="step1"/>
</xsl:variable>
<xsl:apply-templates select="$step-one-result" mode="step2"/>
</xsl:template>
<xsl:template match="text()[contains(.,'°')]" mode="step1">
<xsl:analyze-string select="." regex="°">
<xsl:matching-substring>
<hi xmlns="http://www.tei-c.org/ns/1.0" rend="sup">o</hi>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template match="tei:date[#type='deposition_date' and ./following-sibling::node()[1][. instance of text() and starts-with(.,'.')]]" mode="step2">
<date xmlns="http://www.tei-c.org/ns/1.0">
<xsl:copy-of select="./#*"/>
<xsl:copy-of select="./(* | text())"/>
<xsl:text>.</xsl:text>
</date>
</xsl:template>
<xsl:template match="text()[preceding-sibling::node()[1][./self::tei:date[#type='deposition_date']]][starts-with(.,'.')]" mode="step2">
<xsl:value-of select="substring(.,2)"/>
</xsl:template>
</xsl:stylesheet>
Many thanks in advance.
As you are pushing the whole tree through your modes, I think you forgot to declare
<xsl:mode name="step1" on-no-match="shallow-copy"/>
<xsl:mode name="step2" on-no-match="shallow-copy"/>

XSLT - To move parent attribute value into child first para

Input is like as,
<section counter="yes" level="5">
<title><target id="page92"/></title>
<section counter="yes" level="6">
<title>Standard 12-lead ECG at Rest</title>
<para>The standard ECG is recorded at rest using 12 leads in order to collect as much information as possible:</para>
<listing type="dash">
<litem><para>Standard limb leads according to Einthoven (I, II, III)</para></litem>
Output should be,
<section counter="yes" level="5">
<title><target /></title>
<section counter="yes" level="6">
<title>Standard 12-lead ECG at Rest</title>
<para id="page92">The standard ECG is recorded at rest using 12 leads in order to collect as much information as possible:</para>
<listing type="dash">
<litem><para>Standard limb leads according to Einthoven (I, II, III)</para></litem>
We wrote xslt as shown below,
<xsl:template match="para[1][parent::section[parent::section[not(normalize-space(title))]]]">
<xsl:choose>
<xsl:when test="position() = 1">
<para>
<xsl:attribute name="id" select="ancestor::section[not(normalize-space(title))]/title/target/#id"/>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</para>
</xsl:when>
<xsl:otherwise>
<para>
<xsl:apply-templates select="#*|node()"/>
</para>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
While using above xslt, we are unable to meet expected output.
<section counter="yes" level="5">
<title><target /></title>
<section counter="yes" level="6">
<title>Standard 12-lead ECG at Rest</title>
<para id="page92">The standard ECG is recorded at rest using 12 leads in order to collect as much information as possible:</para>
<listing type="dash">
<litem><para id="page92">Standard limb leads according to Einthoven (I, II, III)</para></litem>
The "page ID" value is repeating on following paragraphs which we didn't required. We need to maintain the page ID only on 1st paragraph.
Could you please guide us.
As for getting the result you want, it should be as simple as
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="section[not(normalize-space(title))]/section/para[1]">
<xsl:copy>
<xsl:attribute name="id" select="ancestor::section[not(normalize-space(title))]/title/target/#id"/>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="section/title[not(normalize-space())]/target/#id"/>
</xsl:transform>

Extract comment data except one in xslt

Input xml is like that,
<section level="2" counter="yes">
<title id="c1_3"><!--1.3--> Reeve’s Prosthesis (1972)</title>
<figure counter="yes" id="f1_1">
<legend><para><!--<emph type="bold">Fig. 1.1</emph>--> Reeve’s prosthesis. (Reproduced with permission from Reeves B, Jobbins B, Dowson D, Wright V. A Total Shoulder Endo-Prosthesis. Eng Med 1972;1(3):64–67.)</para></legend>
<para><!--<inline-figure xlink:href="images/copy.jpg"/>--></para>
</figure>
</section>
Output should be,
<section level="2" counter="yes">
<title id="c1_3">1.3 Reeve’s Prosthesis (1972)</title>
<figure counter="yes" id="f1_1">
<legend><para><emph type="bold">Fig. 1.1</emph> Reeve’s prosthesis. (Reproduced with permission from Reeves B, Jobbins B, Dowson D, Wright V. A Total Shoulder Endo-Prosthesis. Eng Med 1972;1(3):64–67.)</para></legend>
<para><!--<inline-figure xlink:href="images/copy.jpg"/>--></para>
</figure>
</section>
My xslt wrote like this,
<xsl:template match="document//comment()">
<xsl:choose>
<xsl:when test="ancestor::para | ancestor::caption | ancestor::section | ancestor::document">
<xsl:text disable-output-escaping="yes"><comment></xsl:text>
<xsl:variable name="commentText0"><xsl:copy-of select="replace(normalize-space(.),'
',' ')"/></xsl:variable>
<xsl:variable name="commentText2"><xsl:value-of select="replace($commentText0, 'Fig([.]) ', 'Fig. ')" disable-output-escaping="yes"/></xsl:variable>
<xsl:variable name="commentText3"><xsl:value-of select="replace($commentText2, 'Table ', 'Table ')" disable-output-escaping="yes"/></xsl:variable>
<xsl:value-of select="replace($commentText3, 'Formula ', 'Formula ')" disable-output-escaping="yes"/>
<xsl:text disable-output-escaping="yes"></comment></xsl:text>
</xsl:when>
<xsl:when test="comment[contains(.,'inline-figure')]">
<xsl:text disable-output-escaping="yes"><!--</xsl:text>
<xsl:apply-templates select="node() | #*"/>
<xsl:text disable-output-escaping="yes">--></xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
I want to extract comment content except <inline-figure> element. Could you please guide me that how to write code for it.
There's a lot of stuff in your XSLT that seems to bear no relationship to your stated requirement. Assuming you have an XSLT environment where disable-output-escaping is working, you should be able to simply do:
<xsl:template match="comment()">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
<xsl:template match="comment()[contains(., 'inline-figure')]">
<xsl:copy/>
</xsl:template>
I can't see what the rest of your logic is trying to achieve.
Note also that disable-output-escaping generally works only when writing final serialized output from the transformation, not when writing to a variable.
Asuming XSLT 3.0 (as supported by Saxon 9.8 or Altova 2017/2018) you could use
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="document//comment()[not(contains(., 'inline-figure'))]">
<xsl:copy-of select="parse-xml-fragment(.)"/>
</xsl:template>

Replce utf to unicode using xslt with help of database.xml

I want to replace all utf to unicode as below given example using xslt.
Here many utf entities are in my xml and just want to replace then to unicode using xslt with help of a database file which contains all utf and unicode values as well.
Please refer to below given example.
database.xml:-
<entities>
<entity utf8="°" unicode="x00B0" iso="deg" latin1="176"/>
<entity utf8="í" unicode="x00ED" iso="iacute" latin1="237"/>
<entity utf8="é" unicode="x00E9" iso="eacute" latin1="233"/>
<entity utf8="ó" unicode="x00F3" iso="oacute" latin1="243"/>
<entity utf8="â¢" unicode="x2062" iso="InvisibleTimes" latin1="Not Available"/>
</entities>
input:-
<article>
<documentinfo>
<title lang="eng">First report on the contribution of small-sized species to the copepod community structure of the southern Patagonian shelf (Argentina, 47-55°S)</title>
<author>
<lastname>Julieta</lastname>
<firstname>Carolina</firstname>
<middlename>Antacli</middlename>
<fullname>Carolina Antacli Julieta</fullname>
<corresponding>yes</corresponding>
<email>James#gmail.com</email>
<affiliation>Consejo Nacional de Investigaciones Científicas y Técnicas (CONICET). Av. Rivadavia 1917, C1033AAJ, Buenos Aires, Argentina,</affiliation>
<affiliation>Instituto Nacional de Investigación y Desarrollo Pesquero (INIDEP). Paseo Victoria Ocampo 1, B7602HSA, Mar del Plata, Argentina</affiliation>
<affiliation>Instituto de Investigaciones Marinas y Costeras (IIMYC), CONICET-Universidad Nacional de Mar del Plata, Argentina</affiliation>
</author>
Output:-
<article>
<documentinfo>
<title lang="eng">First report on the contribution of small-sized species to the copepod community structure of the southern Patagonian shelf (Argentina, 47-55°S)</title>
<author>
<lastname>Julieta</lastname>
<firstname>Carolina</firstname>
<middlename>Antacli</middlename>
<fullname>Carolina Antacli Julieta</fullname>
<corresponding>yes</corresponding>
<email>James#gmail.com</email>
<affiliation>Consejo Nacional de Investigaciones Científicas y Técnicas (CONICET). Av. Rivadavia 1917, C1033AAJ, Buenos Aires, Argentina,</affiliation>
<affiliation>Instituto Nacional de Investigación y Desarrollo Pesquero (INIDEP). Paseo Victoria Ocampo 1, B7602HSA, Mar del Plata, Argentina</affiliation>
<affiliation>Instituto de Investigaciones Marinas y Costeras (IIMYC), CONICET-Universidad Nacional de Mar del Plata, Argentina</affiliation>
</author>
</documentinfo>
</article>
You can download all files from:- http://www.stylusstudio.com/SSDN/upload/Entities-Replacement.zip also.
If you want to change the encoding of a file, you better use a tool like iconv. E.g.
iconv -f UTF-8 -t UCS-2LE input_UTF8.xml > output_UCS.xml
This info on internationalization and encoding might be useful as well; http://docstore.mik.ua/orelly/xml/jxslt/ch08_06.htm
That said, character-map does work with the input.xml you've uploaded;
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:character-map name="specialsigns">
<xsl:output-character character="°" string="x00B0"/>
<xsl:output-character character="í" string="x00ED"/>
<xsl:output-character character="é" string="x00E9"/>
<xsl:output-character character="ó" string="x00F3"/>
</xsl:character-map>
<xsl:output indent="yes" method="xml" use-character-maps="specialsigns"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is just a modified stylesheet from replace a string with a string with xslt.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vReps" select="document('entities.xml')/entities/*"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" name="replace">
<xsl:param name="pText" select="."/>
<xsl:choose>
<xsl:when test="not($vReps/#utf8[contains($pText, .)])">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="multiReplace">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pReps"
select="$vReps[contains($pText, #utf8)]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="multiReplace">
<xsl:param name="pText"/>
<xsl:param name="pReps"/>
<xsl:choose>
<xsl:when test="$pReps">
<xsl:variable name="escaped">
<xsl:value-of select="concat('&#', $pReps[1]/#unicode, ';')" disable-output-escaping="yes"/>
</xsl:variable>
<xsl:variable name="vRepResult">
<xsl:call-template name="singleReplace">
<xsl:with-param name="pText" select="$pText"/>
<xsl:with-param name="pOld" select="$pReps[1]/#utf8"/>
<xsl:with-param name="pNew" select="$escaped"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="multiReplace">
<xsl:with-param name="pText" select="$vRepResult"/>
<xsl:with-param name="pReps" select="$pReps[position() >1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="singleReplace">
<xsl:param name="pText"/>
<xsl:param name="pOld"/>
<xsl:param name="pNew"/>
<xsl:if test="$pText">
<xsl:choose>
<xsl:when test="not(contains($pText, $pOld))">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($pText, $pOld)"/>
<xsl:value-of select="$pNew" disable-output-escaping="yes"/>
<xsl:call-template name="singleReplace">
<xsl:with-param name="pText" select="substring-after($pText, $pOld)"/>
<xsl:with-param name="pOld" select="$pOld"/>
<xsl:with-param name="pNew" select="$pNew"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied to your inputs, I get:
<article>
<documentinfo>
<title lang="eng">First report on the contribution of small-sized species to the copepod community structure of the southern Patagonian shelf (Argentina, 47-55&#x00B0;S)</title>
<author>
<lastname>Julieta</lastname>
<firstname>Carolina</firstname>
<middlename>Antacli</middlename>
<fullname>Carolina Antacli Julieta</fullname>
<corresponding>yes</corresponding>
<email>James#gmail.com</email>
<affiliation>Consejo Nacional de Investigaciones Cient&#x00ED;ficas y T&#x00E9;cnicas (CONICET). Av. Rivadavia 1917, C1033AAJ, Buenos Aires, Argentina,</affiliation>
<affiliation>Instituto Nacional de Investigaci&#x00F3;n y Desarrollo Pesquero (INIDEP). Paseo Victoria Ocampo 1, B7602HSA, Mar del Plata, Argentina</affiliation>
<affiliation>Instituto de Investigaciones Marinas y Costeras (IIMYC), CONICET-Universidad Nacional de Mar del Plata, Argentina</affiliation>
</author>
</documentinfo>
</article>
All credit goes to Dimitre Novatchev.

Alphabetical sorting with XSLT

Sample XML:
<term>
<name>facies</name>
<translation language="en">facies</translation>
<definition><num>1.</num> cara <num>2.</num> superficie externa, superficie anterior</definition>
</term>
<term>
<name>factores angiógenos</name>
<translation language="en">angiogenic factors</translation>
<definition>descripción de sustancias que favorecen el desarrollo o la formación nueva de vasos sanguíneos</definition>
</term>
<term>
<name>factores de la coagulación</name>
<translation language="en">coagulation factors</translation>
<definition>la cascada de la coagulación de la sangre consta de 12 factores en total, todos ellos necesarios para que funcione bien</definition>
</term>
I have an XSLT file which makes a list of names mapped to name, and a translation if one exists.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="terms">
var allwords = [
<xsl:for-each select="letter">
<xsl:for-each select="term">
<xsl:call-template name="term"></xsl:call-template>
</xsl:for-each>
</xsl:for-each>
['','']
];
</xsl:template>
<xsl:template name="term">
['<xsl:value-of select="name"/>', '<xsl:value-of select="name"/>'],
<xsl:if test="name!=translation">
['<xsl:value-of select="translation"/>', '<xsl:value-of select="name"/>'],
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This works out great, producing the following
var allwords =
[ ['facies', 'facies'],
['factores angiógenos', 'factores angiógenos'],
['angiogenic factors', factores angiógeno],
['factores de la coagulació', 'factores de la coagulació']
etc...
However, as you can see the first part in the array is not in alphabetical order any more, because of the addition of the english word.
It needs to be in alphabetical order, so in this case "angiogenic factors" would be the first element in the array
Any ideas?
Definition and Usage
The <xsl:sort> element is used to sort the output.
Note: <xsl:sort> is always within <xsl:for-each> or <xsl:apply-templates>.
<xsl:sort>
I think it is that simple:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="windows-1252" omit-xml-declaration="yes"/>
<xsl:template match="/*">
var allwords = [
<xsl:apply-templates select="term">
<!-- key to success -->
<xsl:sort select="name"/>
</xsl:apply-templates>
];
</xsl:template>
<xsl:template match="term">
['<xsl:value-of select="name" />',
'<xsl:value-of select="translation[#language='en']" />']
<xsl:if test="position() != last()">,</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output (sorry, had to remove "funny" characters from the test input)
var allwords = [
['facies', 'facies'],
['factores angiogenos', 'angiogenic factors'],
['factores de la coagulacio', 'coagulation factors']
];
(Line breaks and indentation are a little different in original output. I changed them so it looks nice.)
I think you were unaware of <xsl:sort/>, marked with <!-- key to success --> in the XSL stylesheet. ;-)
Yeah, needs a little more thought that just sort :p
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="terms">
var allwords = [
<xsl:for-each select="letter/term/name | letter/term/translation">
<xsl:sort order="ascending" select="."/>
<xsl:call-template name="term"></xsl:call-template>
</xsl:for-each>
['','']
];
</xsl:template>
<xsl:template name="term">
['<xsl:value-of select="."/>', '<xsl:value-of select="./../name"/>'],
</xsl:template>
</xsl:stylesheet>
Basically, you need to be able to select both names and translations, then sort by both of them. Then the template had to be edited a bit, as it was now receiving the child elements rather than the parent.
Here is my best guess at what you are trying to do. Given the following sample XML:
<terms>
<letter name="f">
<term>
<name>facies</name>
<translation language="en">facies</translation>
<definition>
<num>1.</num> cara <num>2.</num> superficie externa, superficie anterior
</definition>
</term>
<term>
<name>factores angiógenos</name>
<translation language="en">angiogenic factors</translation>
<definition>descripción de sustancias que favorecen el desarrollo o la formación nueva de vasos sanguíneos</definition>
</term>
<term>
<name>factores de la coagulación</name>
<translation language="en">coagulation factors</translation>
<definition>la cascada de la coagulación de la sangre consta de 12 factores en total, todos ellos necesarios para que funcione bien</definition>
</term>
</letter>
Sort on the translation for each letter:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="terms">
var allwords = [
<xsl:apply-templates select="letter">
<xsl:sort select="name"/>
</xsl:apply-templates>
];
</xsl:template>
<xsl:template match="letter">
<xsl:apply-templates select="term">
<xsl:sort select="translation"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="term">
['<xsl:value-of select="translation"/>', '<xsl:value-of select="name"/>']
<xsl:if test="not(position() = last())">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>