Getting rid of namespace added to the inner element <i>,<b>, <mpval> - xslt

Namespace getting added to the inner element <i>,<b>, <mpval>. I want to get rid of this namespace.
My XML:
<Container xmlns="http://www.sss.org/schema/"
xmlns:meta="http://www.sss.org/schema/tangier/metadata">
<cs-properties>
My Parent level text 1
<mp>
text1 of first child <b> in bold</b>
<mpval>36-37</mpval>
text2 of child <i> in italic </i>
</mp>
My Parent level text2 in <i>italic</i> also in <b>bold </b>
</cs-properties>
</Container>
When I apply below XSL, I get namespace added to <i> element. Want to get rid of it.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sub="http://www.sss.org/schema"
exclude-result-prefixes="xsl sub">
<xsl:variable name="ns" select="'http://www.sss.org/schema/'" />
<xsl:output indent="no" omit-xml-declaration="yes"/>
<xsl:variable name="inlineElements" select="'b','i','sub','sup'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sub:cs-properties">
<!--<properties xmlns= "{$ns}">-->
<xsl:element name="cs-properties" namespace="{$ns}" >
<xsl:for-each-group select="node()" group-adjacent="self::text() or self::node()
[name()=$inlineElements]">
<xsl:choose>
<xsl:when test="current-grouping-key()=true()">
<parenttext>
<xsl:copy-of select="current-group()" copy-namespaces="no" />
</parenttext>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
<!--</properties>-->
</xsl:element>
</xsl:template>
<xsl:template match="sub:mp|sub:abs-max">
<xsl:element name="{name()}">
<xsl:for-each-group select="node()" group-adjacent="self::text() or self::node()
[name()=$inlineElements]">
<xsl:choose>
<xsl:when test="current-grouping-key()=true()">
<childtext>
<xsl:copy-of select="current-group()" copy-namespaces="no"/>
</childtext>
</xsl:when>
<xsl:otherwise>
<!--<xsl:apply-templates select="."/>-->
<xsl:copy-of select="current-group()" copy-namespaces="no"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result:
<Container xmlns="http://www.sss.org/schema"
xmlns:meta="http://www.sss.org/schema/tangier/metadata"><cs-properties><parenttext
xmlns="">
My Parent level text 1
</parenttext><mp xmlns=""><childtext>
text1 of first child <b xmlns="http://www.sss.org/schema"> in
bold</b></childtext><mpval xmlns="http://www.sss.org/schema">36-
37</mpval><childtext>
text2 of child <i xmlns="http://www.sss.org/schema"> in italic </i>
</childtext></mp><parenttext xmlns="">
My Parent level text2 in <i
xmlns="http://www.sss.org/schema">italic</i> also in <b
xmlns="http://www.sss.org/schema">bold </b></parenttext></cs-
properties></Container>

You can avoid the <parenttext xmlns=""> either by using <parenttext xmlns="http://www.sss.org/schema/"> in your markup or by putting xmlns="http://www.sss.org/schema/" on the styleheet's root element. The latter would affect all result elements which might be needed if you have them in more places in the stylesheet.

Related

duplicate content in xsl

I am very new in xsl. I was trying to add the <quote> tag in between the <para>.tag. but the output printing twice.
Here is my xsl code
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*" mode="pretrans">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[count(child::node())=0]" mode="pretrans"/>
<xsl:template match="doc">
<poc>
<xsl:apply-templates/>
</poc>
</xsl:template>
<xsl:template match="text">
<chapter>
<xsl:variable name="pos" select="count(child::node()[#style='H5']/preceding-sibling::p)+1"/>
<xsl:apply-templates select="child::node()[position()<$pos]" mode="presec"/>
<section>
<xsl:variable name="nodesets" >
<xsl:apply-templates select="child::node()[position()>=$pos]" mode="pretrans"/>
</xsl:variable>
<xsl:apply-templates select="$nodesets" mode="postsec"/> <!---->
</section>
</chapter>
</xsl:template>
<xsl:template match="p" mode="presec">
<xsl:choose>
<xsl:when test="#style='H2'">
<title><xsl:apply-templates/></title>
</xsl:when>
<xsl:when test="#style='H4'">
<subdivision>
<title><xsl:apply-templates/></title>
</subdivision>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="p" mode="postsec">
<xsl:variable name="pos" select="count(preceding-sibling::p[#style='H5'][1]/preceding-sibling::p)+1"/>
<xsl:variable name="pos" select="count(preceding-sibling::p)+1"/>
<xsl:variable name="styleblock" select="count(preceding-sibling::p[#style='BlockStyle'][1]/preceding-sibling::p)+1"/>
<xsl:choose>
<xsl:when test="#style='H5'">
<title><xsl:apply-templates/></title>
</xsl:when>
<xsl:when test="count(child::node())=0"/>
<xsl:otherwise>
<paragraph>
<xsl:if test="#style='BlockStyle'">
<quotes>
<xsl:apply-templates/>
</quotes>
</xsl:if>
<xsl:apply-templates/>
</paragraph>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
expected output:
<poc>
<chapter>
<section>
<paragraph>
<quote>
Hi welcome to new year 2022
</quote>
Hi welcome to new year 2022
</paragraph>
</section>
</chapter>
</poc>
The message is printing twice.
can anyone help me in this.
Depending on your needs delete the first or the second
<paragraph>
<xsl:if test="#style='BlockStyle'">
<quotes>
<xsl:apply-templates/><!-- First -->
</quotes>
</xsl:if>
<xsl:apply-templates/><!-- Second -->
</paragraph>

XSLT - Can't get data from node, when using xml:when

I have a problem getting data from a node, when I'm using xml:choose and xml:when. I only get the result NaN or the value from the main xml-file, even if.
Part of the XML-file:
<?xml version="1.0" encoding="UTF-8"?>
<Job xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<Invoice>
<InvoiceLine>
<LineNo>1</LineNo>
<QtyInSecondUnit>56</QtyInSecondUnit>
<Quantity>56</Quantity>
<CustTaric>
<StatNo>34011100</StatNo>
<IssuingCountry>GB</IssuingCountry>
</CustTaric>
</InvoiceLine>
<InvoiceLine>
<LineNo>2</LineNo>
<QtyInSecondUnit>22</QtyInSecondUnit>
<Quantity>0</Quantity>
<CustTaric>
<StatNo>44152020</StatNo>
<IssuingCountry>GB</IssuingCountry>
</CustTaric>
</InvoiceLine>
</Invoice>
</Job>
Part of the XSLT-file:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="Job/Invoice"/>
</xsl:template>
<xsl:template match="Job/Invoice/InvoiceLine">
<xsl:apply-templates select="QtyInSecondUnit"/>
<xsl:apply-templates select="Quantity"/>
<xsl:apply-templates select="CustTaric/StatNo"/>
</xsl:template>
<xsl:template match="QtyInSecondUnit">
<xsl:choose>
<xsl:when test="/Job/Invoice/InvoiceLine/CustTaric/StatNo = '44152020'">
<xsl:value-of select="number(translate(Job/Invoice/InvoiceLine/NetMass,',','.')) div 25"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Quantity">
<xsl:choose>
<xsl:when test="/Job/Invoice/InvoiceLine/CustTaric/StatNo = '44152020'">
<xsl:value-of select="'0'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="CustTaric/StatNo">
<xsl:value-of select="."/>
</xsl:template>
Hope there is somebody how can tell me (the noob) what I'm doing wrong here?
Here's the line producing NaN
<xsl:value-of select="number(translate(Job/Invoice/InvoiceLine/NetMass,',','.')) div 25"/>
There are two problems (one of which is probably where you have over-simplified your XML)
The xpath expression you are using will be relative to the current node you are positioned on. There is no Job element under the current QtyInSecondUnit element
There is no NetMass element in your XML in your question
Assuming NetMass does exist in your actual XML, and is a child of the parent InvoiceLine the expression you want is this
<xsl:value-of select="number(translate(../NetMass,',','.')) div 25"/>
There is also an issue with your xsl:when (possibly)
<xsl:when test="/Job/Invoice/InvoiceLine/CustTaric/StatNo = '44152020'">
This will test for any CustTaric/StatNo anywhere in the document. Perhaps you only want to test for the one in the current InvoiceLine? If so, do this...
<xsl:when test="../CustTaric/StatNo = '44152020'">
Note, you could rewrite your XSLT to put the logic in template matches, rather than xsl:choose
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Job/Invoice/InvoiceLine">
<xsl:apply-templates select="QtyInSecondUnit"/>
<xsl:apply-templates select="Quantity"/>
<xsl:apply-templates select="CustTaric/StatNo"/>
</xsl:template>
<xsl:template match="InvoiceLine[CustTaric/StatNo = '44152020']/QtyInSecondUnit">
<xsl:value-of select="number(translate(../NetMass,',','.')) div 25"/>
</xsl:template>
<xsl:template match="QtyInSecondUnit">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="InvoiceLine[CustTaric/StatNo = '44152020']/Quantity">
<xsl:value-of select="'0'"/>
</xsl:template>
<xsl:template match="Quantity">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="CustTaric/StatNo">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Strictly speaking, the templates that just do <xsl:value-of select="."/> can be removed, as XSLT's built-in templates will do exactly the same thing if there is no matching template in the XSLT.

ce:chem tag to be converted to mml:math

Is there a way to convert these elsevier tags into mml:math tags?
<ce:chem>PEG<ce:inf>BOUND</ce:inf>
<ce:hsp sp="0.25"/>=<ce:hsp sp="0.25"/>PEG<ce:inf>TOT</ce:inf>
<ce:hsp sp="0.25"/>-<ce:hsp sp="0.25"/>PEG<ce:inf>FINAL</ce:inf>
</ce:chem>
Try this:
XML: (Ensure ce:chem content should not have line breaks and comment text. grouping functions can do better than my code, but I placed this jsut to meet the requirement)
<article xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML">
<p>
<ce:chem>PEG<ce:inf>BOUND</ce:inf><ce:hsp sp="0.25"/>=<ce:hsp sp="0.25"/>PEG<ce:inf>TOT</ce:inf><ce:hsp sp="0.25"/>-<ce:hsp sp="0.25"/>PEG<ce:inf>FINAL</ce:inf>A<ce:sup>2</ce:sup></ce:chem>
</p>
</article>
XSLT 2.0: (latest xslt)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns:ja="http://www.elsevier.com/xml/ja/dtd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:template>
<xsl:template match="ce:chem">
<xsl:copy>
<xsl:apply-templates select="node()[not(self::ce:inf)][not(self::ce:sup)] | #*" />
</xsl:copy>
</xsl:template>
<xsl:key name="ksub" match="ce:inf" use="generate-id(preceding-sibling::node()[1][self::text()])"/>
<xsl:key name="ksup" match="ce:sup" use="generate-id(preceding-sibling::node()[1][self::text()])"/>
<xsl:template match="ce:chem/text()">
<xsl:choose>
<xsl:when test="following-sibling::node()[1][name()='ce:inf']">
<xsl:element name="ce:msub">
<xsl:call-template name="tempNameElements"/>
<xsl:apply-templates select="key('ksub', generate-id())" />
</xsl:element>
</xsl:when>
<xsl:when test="following-sibling::node()[1][name()='ce:sup']">
<xsl:element name="ce:msup">
<xsl:call-template name="tempNameElements"/>
<xsl:apply-templates select="key('ksup', generate-id())" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="tempNameElements"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ce:hsp">
<xsl:element name="mml:mspace">
<xsl:attribute name="width"><xsl:value-of select="#sp"/></xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="ce:inf">
<xsl:for-each select="node()">
<xsl:call-template name="tempNameElements"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="ce:sup">
<xsl:for-each select="node()">
<xsl:call-template name="tempNameElements"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="tempNameElements">
<xsl:choose>
<xsl:when test="starts-with(replace(self::text(), '([A-z]+)', 'A'), 'A')">
<xsl:element name="mml:mi"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:when test="starts-with(replace(self::text(), '([0-9]+)', '9'), '9')">
<xsl:element name="mml:mn"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:when test="matches(self::text(), '^(\(|\[|\{|=|\-)$')">
<xsl:element name="mml:mo"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:otherwise><xsl:element name="mml:mtext"><xsl:value-of select="."/></xsl:element></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<article xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML">
<p>
<ce:chem>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>BOUND</mml:mi>
</ce:msub>
<mml:mspace width="0.25"/>
<mml:mo>=</mml:mo>
<mml:mspace width="0.25"/>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>TOT</mml:mi>
</ce:msub>
<mml:mspace width="0.25"/>
<mml:mo>-</mml:mo>
<mml:mspace width="0.25"/>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>FINAL</mml:mi>
</ce:msub>
<ce:msup>
<mml:mi>A</mml:mi>
<mml:mn>2</mml:mn>
</ce:msup>
</ce:chem>
</p>
</article>

Removing the root element using xslt

I have an xml schema that has a structure like
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
.
.
.
</Level>
I want to remove the <Level> tag. How am I supposed to do that, with the help of xslt.
The standard answer to any "how do I keep most of my XML the same but tweak some little bits of it" question is "use an identity template and then override it for the specific things you want to change"
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- omit the <?xml?> line in the output, it won't be well-formed anyway -->
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
but as Mr Lister points out in the comments, this will leave you with something that is not well formed XML, as it will have more than one document element.
When I apply this stylesheet on the input XML
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
</Level>
it produces the result
<Level1>...data...</Level1>
<Level2>...data...</Level2>
If you want to store all child elements of the document element in a variable, you can do something like:
<xsl:variable name="myVar" select="/*/*"/>
If, however, you want to the stylesheet to produce a string, this might be a solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="*">
<!-- We write the opening tag -->
<xsl:value-of select="concat('<',local-name())"/>
<!-- Now, all attributes -->
<xsl:apply-templates select="#*"/>
<!-- Depending on whether we have an empty element or not,
we're adding the content or closing it immediately -->
<xsl:choose>
<xsl:when test="node()">
<!-- Close opening tag -->
<xsl:value-of select="'>'"/>
<!-- Add the content -->
<xsl:apply-templates select="node()"/>
<!-- Write closing tag -->
<xsl:value-of select="concat('</',local-name(),'>')"/>
</xsl:when>
<xsl:otherwise>
<!-- Create empty element by closing tag immediately -->
<xsl:value-of select="'/>'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*">
<!-- Write an attribute -->
<xsl:value-of select="concat(' ',local-name(),'="',.,'"')"/>
</xsl:template>
</xsl:stylesheet>
It produces text (and therefore you won't get non-well-formed XML). It's a little over-simplified because it does not handle namespaces, comments, processing instructions and quotes in attributes. If your input XML contains any of these, you'd have to refine the stylesheet.
I have created a new XSLT definition which fulfill my requremtn with the help of your code
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="text" omit-xml-declaration="yes"/>
<xsl:variable name="nl">
<xsl:text/>
</xsl:variable>
<xsl:variable name="tb">
<xsl:text/>
</xsl:variable>
<xsl:template match="/*">
<!-- Open the root array -->
<xsl:text>[{</xsl:text>
<xsl:value-of select="$nl"/>
<!-- Process all the child nodes of the root -->
<xsl:apply-templates mode="detect" select="*">
<xsl:with-param name="indent" select="$tb"/>
</xsl:apply-templates>
<!-- Close the root array -->
<xsl:value-of select="$nl"/>
<xsl:text>}]</xsl:text>
</xsl:template>
<xsl:template match="*" mode="detect">
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>" : [</xsl:text>
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(#*) > 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : [<xsl:apply-templates
mode="obj-content" select="."/>
<xsl:if test="count(following-sibling::*) > 0">], </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates mode="attr" select="#*"/>
<xsl:if test="count(#*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates mode="detect" select="./*"/>
<xsl:if test="count(child::*) = 0 and text() and not(#*)">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and #*">
<xsl:text>: "</xsl:text>
<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="#*" mode="attr">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/#TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))">
<xsl:copy-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT Gouping, for-each and implicated loop. How to eliminate duplicates?

Hope find a guru's help to figure out the next problem.
I have two xml files. Firts one here (text.xml):
<text>
<ref>Author1, Title1, Date1</ref>
<ref>Author75, Title75, Date2</ref>
<ref>Author2, Title2, Date2</ref>
<ref>Author3, Title3, Date3</ref>
<text>
And the second one like this (list.xml):
<list>
<bibl xml:id="1"><author>Author1</author><date>Date1</date></bibl>
<bibl xml:id="2"><author>Author2</author><date>Date2</date></bibl>
<bibl xml:id="3"><author>Author3</author><date>Date3</date></bibl>
</list>
I want to query text.xml and check against list.xml to add #xml:id (from list.xml) to <ref> (from text.xml) wich contain same Author and Date. If not, then just copy original <ref>.
So I want to obtain:
<ref xml:id="1">Author1, Title1, Date1</ref>
<ref>Author75, Title75, Date2</ref>
<ref xml:id="2>Author2, Title2, Date2</ref>
etc.
My XSLT identify well all correpondence:
<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="ref">
<xsl:variable name="ref" select="."/>
<xsl:for-each select="document('list.xml')//bibl">
<xsl:variable name="bibl" select="."/>
<xsl:variable name="author" select="author"/>
<xsl:variable name="date" select="date"/>
<xsl:choose>
<xsl:when test="contains($ref, $author) and contains($ref, $date)">
<ref>
<xsl:attribute name="xml:id">
<xsl:value-of select="$bibl/#xml:id"/>
</xsl:attribute>
<xsl:value-of select="$ref"/>
</ref>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$ref"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
But, then there aren't correpondence it's not just copy right <ref>, but copy all <ref> the number of time I have <bibl> nodes in the second file.
So problem is in <xsl:otherwise><xsl:copy-of select="$ref"/></xsl:otherwise>.
Any ideas how I can obtain only this distinct value I need? I know it's must be very simple actually and I try key, generate-id, for-each-group, distinct-values, but can't figure it out.
The problem is that you are creating a ref element for each iteration of the for-each loop whether there is a match or not.
What you need to do in this case is create the ref element outside of the for-each and then only create the id attribute for the matching element inside the loop
<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="ref">
<xsl:variable name="ref" select="."/>
<ref>
<xsl:apply-templates select="#* "/>
<xsl:for-each select="document('list.xml')//bibl">
<xsl:variable name="bibl" select="."/>
<xsl:variable name="author" select="author"/>
<xsl:variable name="date" select="date"/>
<xsl:choose>
<xsl:when test="contains($ref, $author) and contains($ref, $date)">
<xsl:attribute name="xml:id">
<xsl:value-of select="$bibl/#xml:id"/>
</xsl:attribute>
</xsl:when>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="node()"/>
</ref>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<text>
<ref xml:id="1">Author1, Title1, Date1</ref>
<ref>Author75, Title75, Date2</ref>
<ref xml:id="2">Author2, Title2, Date2</ref>
<ref xml:id="3">Author3, Title3, Date3</ref>
</text>
However, your current method is not very efficient, as for each ref element you are iterating over all bibl elements. Another approach would be to extract the author and date from the ref elements, and then look up the bibl element directly
<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="ref">
<xsl:variable name="author" select="normalize-space(substring-before(., ','))"/>
<xsl:variable name="date" select="normalize-space(substring-after(substring-after(., ','), ','))"/>
<ref>
<xsl:apply-templates select="#* "/>
<xsl:apply-templates select="document('list.xml')//bibl[author=$author][date=$date]"/>
<xsl:apply-templates select="node()"/>
</ref>
</xsl:template>
<xsl:template match="bibl">
<xsl:attribute name="xml:id">
<xsl:value-of select="#xml:id"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
This should also give the same results.
EDIT:
humm...too much focus on xsl syntax, i should have seen that earlier...
you have an implicated outer loop over each ref and an inner loop over each bibl. You generate one element for every bibl for every ref regardless of match or no match.
So, instead of the xsl:otherwise you need a check after the for-each loop to see if there was no match and do the copy-of if neccessary.
Not sure how to do the check, though..maybe using position() and count() of the generated <ref>s, sorry...don't have any more time to think about this right now.
not really an explanation, but a workaround:
<xsl:otherwise>
<ref>
<xsl:value-of select="$ref"/>
</ref>
</xsl:otherwise>
My guess is that the problem lies in $ref, which is not a xpath expression at that moment (if i remember xsl-t correctly)