Alphabetical sorting with XSLT - 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>

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"/>

How to remove duplicate entry - XSLT

I am try to remove duplicate entry after entity § and if contains the , in entry and after tokenize the start-with the ( round bracket then entry e.g (17200(b)(2), (4)–(6)) s/b e.g (<p>17200(b)(2)</p><p>17200(b)(4)–(6)</p>).
Input XML
<root>
<p>CC §1(a), (b), (c)</p>
<p>Civil Code §1(a), (b)</p>
<p>CC §§2(a)</p>
<p>Civil Code §3(a)</p>
<p>CC §1(c)</p>
<p>Civil Code §1(a), (b), (c)</p>
<p>Civil Code §17200(b)(2), (4)–(6), (8), (12), (16), (20), and (21)</p>
</root>
Expected Output
<root>
<sec specific-use="CC">
<title content-type="Sta_Head3">CIVIL CODE</title>
<p>1(a)</p>
<p>1(b)</p>
<p>1(c)</p>
<p>2(a)</p>
<p>3(a)</p>
<p>17200(b)(2)</p>
<p>17200(b)(4)–(6)</p>
<p>17200(b)(8)</p>
<p>17200(b)(12)</p>
<p>17200(b)(16)</p>
<p>17200(b)(20)</p>
<p>17200(b)(21)</p>
</sec>
</root>
XSLT Code
<xsl:template match="root">
<xsl:copy>
<xsl:for-each-group select="p[(starts-with(., 'CC ') or starts-with(., 'Civil Code'))]" group-by="replace(substring-before(., ' §'), 'Civil Code', 'CC')">
<xsl:text>
</xsl:text>
<sec specific-use="{current-grouping-key()}">
<xsl:text>
</xsl:text>
<title content-type="Sta_Head3">CIVIL CODE</title>
<xsl:for-each-group select="current-group()" group-by="replace(substring-after(., '§'), '§', '')">
<xsl:sort select="replace(current-grouping-key(), '[^0-9.].*$', '')" data-type="number" order="ascending"/>
<xsl:for-each
select="distinct-values(
current-grouping-key() !
(let $tokens := tokenize(current-grouping-key(), ', and |, | and ')
return (head($tokens), tail($tokens) ! (substring-before(head($tokens), '(') || .)))
)" expand-text="yes">
<p>{.}</p>
</xsl:for-each>
</xsl:for-each-group>
</sec>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
You could do it like this, in a two-step approach where you first compute the list of existing elements and then use a for-each-group to remove duplicates.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="listP">
<xsl:apply-templates select="root/p"/>
</xsl:variable>
<xsl:for-each-group select="$listP" group-by="p">
<p><xsl:value-of select="current-grouping-key()"/></p>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="p">
<xsl:variable name="input" select="replace(substring-after(.,'§'),'§','')"/>
<xsl:variable name="chapter" select="substring-before($input,'(')"/>
<xsl:for-each select="tokenize(substring-after($input, $chapter),',')">
<p><xsl:value-of select="concat($chapter,replace(replace(.,' ',''),'and',''))"/></p>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/gVrvcxQ

XSLT and numbers in attributes

I have an xml-fragment like this:
<p rend="noIndent">eget <seg type="comStart" n="com123"/>kunde<seg type="comEnd" n="com123"/> der i Anledning af disse <seg type="comStart" n="com13"/>kunde<seg type="comEnd" n="com13"/> Smaadigte være at s</p>
I want to change the numbers in the n-attribut and make the numbers starts with 1. The values in the n-attribut shold be the same for each pair of comStat and comEnd in the type-attribute, ending up with this result:
<p rend="noIndent">eget <seg type="comStart" n="com1"/>havde<seg type="comEnd" n="com1"/> der i Anledning af disse <seg type="comStart" n="com2"/>kunde<seg type="comEnd" n="com2"/> Smaadigte være at s</p>
How do I match the corresponding n-attributs in xslt?
KSR
If there are always pairs you can simply create the number using xsl:number for the comStart element:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="*[#type = ('comStart', 'comEnd')]/#n">
<xsl:attribute name="{name()}">n<xsl:number level="any" count="*[#type = 'comStart']"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
http://xsltfiddle.liberty-development.net/eiQZDbm
respectively
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#type = 'comStart' or #type = 'comEnd']/#n">
<xsl:attribute name="{name()}">n<xsl:number level="any" count="*[#type = 'comStart']"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
for XSLT 1 (http://xsltfiddle.liberty-development.net/eiQZDbm/1).

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.

Grouping *almost* unique ids

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.