I have xml like this:
<article>
<title> Test title - <literal> Compulsory - </literal> <fn> ABC </fn>
<comments> a comment</comments>
</title>
</article>
I want to get all child node + self text in a variable
e.g.
$full_title = "Test title - Compulsory - ABC"
Except comments node text.
Following is my unsuccessful try where i miss title node text.
<xsl:template name="test">
<xsl:variable name="full_title" select="article/title/*[not(self::comments)][1]" />
<xsl:variable name="width" select="45" />
<xsl:choose>
<xsl:when test="string-length($full_title) > $width">
<xsl:value-of select="concat(substring($full_title,1,$width),'..')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$full_title"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Change * to node(). That will select both elements and text nodes that are children of the <title> element. Then take out the [1] since you want all children of <title>:
<xsl:variable name="full_title"
select="string-join(article/title/node()[not(self::comments)], '')" />
A more reliable way to do it, so that you won't get tripped up if you have multiple levels under <title> and <comments> elements occur as grandchildren, would be this:
<xsl:variable name="full_title"
select="string-join(article/title//text()[not(ancestor::comments)], '')" />
Update:
Since you want the variable to hold a string value, and since you're passing it to functions like concat() and string-length() which cannot take a sequence of multiple nodes as a first argument, using string-join(..., '') around the sequence converts it to a string by concatenating the string values of each node.
Try this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:variable name="full-text">
<xsl:apply-templates select="//*[not(self::comments)]"
mode="no-comments"/>
</xsl:variable>
<xsl:value-of select="$full-text"/><!-- just for debug-->
</xsl:template >
<xsl:template match="*" mode="no-comments">
<xsl:value-of select="text()"/>
</xsl:template>
</xsl:stylesheet>
attribute mode used only for clarity
Related
fairly new to xlst and trying to get my head around things..
Here is a sample of the source data (heavily trimmed as there is MB)
<Chemical id="000023950" displayFormula="" displayName="">
</Chemical>
<Chemical id="000035676" displayFormula="C39-H45-N2-O6" displayName="Dimethyltubocurarine">
</Chemical>
<Chemical id="000036282" displayFormula="" displayName="INDEX NAME NOT YET ASSIGNED">
</Chemical>
What I am trying to do is extract the displayname and the id of each chemical into a csv
Right now I can extract them using the below code - its pretty crude to be honest
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:variable name="FS">
<!-- Field seperator -->
<xsl:text>;</xsl:text>
</xsl:variable>
<xsl:variable name="LT">
<!-- Line terminator -->
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:for-each select="//Chemical">
<xsl:text>"</xsl:text>
<xsl:value-of select="#displayName" />
<xsl:text>"</xsl:text>
<xsl:text>,</xsl:text>
<xsl:text>"</xsl:text>https://chem.nlm.nih.gov/chemidplus/sid/startswith/<xsl:value-of select="#id" />
<xsl:text>"</xsl:text>
<xsl:text>,"nlm"</xsl:text>
<xsl:value-of select="$LT" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
My question is..
How can I skip branches that have the displayName attribute as "" or as "INDEX NAME NOT YET ASSIGNED"?
Thanks
The <xsl:for-each> can be modified to
<xsl:for-each select="//Chemical[#displayName != '' and #displayName != 'INDEX NAME NOT YET ASSIGNED']">
This will filter out the values of #displayName as required and just produce the output
Dimethyltubocurarine
I read lot of articles but did not find a conclusive help to my problem.
I have an XML document to which I apply an xslt to get a csv file as output.
I send a parameter to my xsl transformation to filter the target nodes to apply the templates.
The xml document looks like that (I removed some unuseful nodes for comprehension):
<GetMOTransactionsResponse xmlns="http://www.exane.com/pott" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exane.com/pott PoTTMOTransaction.xsd">
<MOTransaction>
<Transaction VersionNumber="2" TradeDate="2013-11-20">
<TransactionId Type="Risque">32164597</TransactionId>
<InternalTransaction Type="Switch">
<BookCounterparty>
<Id Type="Risque">94</Id>
</BookCounterparty>
</InternalTransaction>
<SalesPerson>
<Id Type="Risque">-1</Id>
</SalesPerson>
</Transaction>
<GrossPrice>58.92</GrossPrice>
<MOAccount Account="TO1E" />
<Entity>0021</Entity>
</MOTransaction>
<MOTransaction>
<Transaction VersionNumber="1" TradeDate="2013-11-20">
<TransactionId Type="Risque">32164598</TransactionId>
<SalesPerson>
<Id Type="Risque">-1</Id>
</SalesPerson>
</Transaction>
<GrossPrice>58.92</GrossPrice>
<MOAccount Account="TO3E" />
<Entity>0021</Entity>
</MOTransaction>
</GetMOTransactionsResponse>
My xslt is below (sorry it's quite long, and I write it more simple than it really is):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:pott="http://www.exane.com/pott">
<xsl:output method="text" omit-xml-declaration="no" indent="no" />
<xsl:param name="instrumentalSystem"></xsl:param>
<xsl:template name="abs">
<xsl:param name="n" />
<xsl:choose>
<xsl:when test="$n = 0">
<xsl:text>0</xsl:text>
</xsl:when>
<xsl:when test="$n > 0">
<xsl:value-of select="format-number($n, '#')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="format-number(0 - $n, '#')" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="outputFormat">
<!--Declaration of variables-->
<xsl:variable name="GrossPrice" select="pott:GrossPrice" />
<xsl:variable name="TransactionId" select="pott:Transaction/pott:TransactionId[#Type='Risque']" />
<xsl:variable name="VersionNumber" select="pott:Transaction/#VersionNumber" />
<!--Set tags values-->
<xsl:value-of select="$Entity" />
<xsl:text>;</xsl:text>
<xsl:value-of select="concat('0000000', pott:MOAccount/#Account) "/>
<xsl:text>;</xsl:text>
<xsl:text>;</xsl:text>
<xsl:value-of select="$TransactionId" />
<xsl:text>;</xsl:text>
<xsl:value-of select="$VersionNumber" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="/">
<xsl:choose>
<!-- BB -->
<xsl:when test="$instrumentalSystem = 'BB'">
<!--xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction/pott:Transaction[pott:InternalTransaction]"-->
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction/pott:Transaction[pott:InternalTransaction]">
<xsl:call-template name="outputFormat"></xsl:call-template>
</xsl:for-each>
</xsl:when>
<!-- CP -->
<xsl:when test="$instrumentalSystem = 'CP'">
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction/pott:Transaction[not(pott:InternalTransaction)]">
<xsl:call-template name="outputFormat"></xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
If parameter = BB, I want to select MOTransaction nodes that have a child Transaction that contains a InternalTransaction node.
If parameter = CP, I want to select MOTransaction nodes that don't have a child Transaction that contains a InternalTransaction node
When I write
pott:GetMOTransactionsResponse/pott:MOTransaction/pott:Transaction[pott:InternalTransaction], I get the Transaction nodes and not the MOTransaction nodes
I think I am not very far from the expected result, but despite all my attempts, I fail.
If anyone can help me.
I hope being clear, otherwise I can give more information.
Looking at one of xsl:for-each statements, you are doing this
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction/pott:Transaction[pott:InternalTransaction]">
You say you want to select MOTransaction elements, but it is actually selecting the child Transaction elements. To match the logic you require, it should be this
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction[pott:Transaction[pott:InternalTransaction]]">
In fact, this should also work
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction[pott:Transaction/pott:InternalTransaction]">
Similarly, for the second statement (in the case of the parameter being "CP"), it could look like this
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction[pott:Transaction[not(pott:InternalTransaction)]]">
Alternatively, it could look like this
<xsl:for-each select="pott:GetMOTransactionsResponse/pott:MOTransaction[not(pott:Transaction/pott:InternalTransaction)]">
They are not quite the same though, as the first will only include MOTransaction elements that have Transaction child elements, whereas the second will include MOTransaction that don't have any Transaction childs at all.
As a slight aside, you don't really need to use an xsl:for-each and xsl:call-template here. It might be better to use template matching.
Firstly, try changing the named template <xsl:template name="outputFormat"> to this
<xsl:template match="pott:MOTransaction">
Then, you can re-write you merge the xsl:for-each and xsl:call-template into a single xsl:apply-templates call.
<xsl:apply-template select="pott:GetMOTransactionsResponse/pott:MOTransaction[pott:Transaction/pott:InternalTransaction]" />
I am in need to transform the below coding using XSLT 1.0 based on the separators attributes given. The text should be separated based on the separators given:
Input:
<chapter xmlns="http://www.w3.org/1998/Math/MathML">
<math display="inline"><mfenced separators=", : . ;"><mn>1</mn><mtext>b</mtext><mo>%</mo><mi>d</mi><mi>e</mi></mfenced></math>
<math display="inline"><mfenced separators=", ;"><mi>a</mi><mi>b</mi><mi>c</mi><mi>d</mi><mi>e</mi></mfenced></math>
<math display="inline"><mfenced separators=", : . ; ; : . ;"><mi>a</mi><mi>b</mi><mi>c</mi><mi>d</mi><mi>e</mi></mfenced></math>
</chapter>
output required:
1,b:%.d;e
a,b;c;d;e
a,b:c.d;e
Also please note that if there are too many separator characters, the extra ones are ignored. If separator characters are given, but there are too few, the last one is repeated as necessary
I could not able to get the output only if the separator characters are lesser than the child elements.
XSLT 1.0 tried:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML">
<xsl:template match="m:mfenced">
<xsl:variable name="text" select="#separators"/>
<xsl:for-each select="child::*">
<xsl:apply-templates/>
<xsl:choose>
<xsl:when test="contains($text,' ')">
<xsl:variable name="attr" select="string-length(translate($text, ' ', ''))"/>
<xsl:variable name="ch" select="count(parent::*/child::*)-1"/>
<xsl:if test="$ch=$attr"><xsl:value-of select="substring($text,count(preceding-sibling::*)+position(),1)"/></xsl:if>
<xsl:if test="$ch gt $attr">
<xsl:if test="not(substring($text,count(preceding-sibling::*)+position(),1)='')"><xsl:value-of select="substring($text,count(preceding-sibling::*)+position(),1)"/></xsl:if>
<xsl:if test="(substring($text,count(preceding-sibling::*)+position(),1)='')"><xsl:value-of select="substring($text,count(preceding-sibling::*)+1,1)"/></xsl:if>
</xsl:if>
<xsl:if test="$ch lt $attr and count(following-sibling::*)>0"><xsl:value-of select="substring($text,count(preceding-sibling::*)+position(),1)"/></xsl:if>
</xsl:when>
<xsl:otherwise><xsl:if test="count(following-sibling::*)>0"><xsl:value-of select="$text"/></xsl:if></xsl:otherwise></xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The following solution is based on obtaining the position of each <m:mi> within the <m:fenced> elements to obtain the next operator to be outputted.
Note. I am assuming that the string length used to represent each operator is 1.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML">
<xsl:output method="text" />
<!-- Ignore all text nodes (just for demo) -->
<xsl:template match="text()" />
<xsl:template match="m:mfenced">
<!-- Print children values and operators -->
<xsl:apply-templates select="*" mode="list-op">
<xsl:with-param name="separator-str" select="#separators" />
<xsl:with-param name="separator-len" select="string-length(#separators)" />
</xsl:apply-templates>
<!-- Print new line -->
<xsl:text>
</xsl:text>
</xsl:template>
<!-- Last m:mi elements for each m:mfenced are just printed -->
<xsl:template match="*[last()]" mode="list-op">
<xsl:value-of select="."/>
</xsl:template>
<!-- In this template we use the position() function to calculate the next
operator that is going to be outputted -->
<xsl:template match="*" mode="list-op">
<xsl:param name="separator-str" />
<!-- This parameter is not required, but allows us to cache
the length of the separators string instead of calculating it
for each m:mi element -->
<xsl:param name="separator-len" />
<!-- Print current value -->
<xsl:value-of select="." />
<!-- Calculate the separator position within the string -->
<xsl:variable name="string-position" select="2*position() - 1" />
<!-- Check if the position oveflows the position in the array, and
if it does, print the last separator in the string. -->
<xsl:choose>
<xsl:when test="$separator-len >= $string-position">
<xsl:value-of select="substring($separator-str, $string-position, 1)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($separator-str, $separator-len)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
How do I select only n number of comments element using xsl. I am able to select n number of characters but that snaps in the middle of a word and makes it look ugly.
I have been able to get a count of total number of words in 'comments' node but not sure how to only show like 15 words if there are like a total of 20 there.
<div class="NewsDescription">
<xsl:value-of select="string-length(translate(normalize-space(#Comments),'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',''))+1" />
<xsl:value-of select="substring(#Comments,0,120)"/>
<xsl:if test="string-length(#Comments) > 120">…</xsl:if>
Read More
</div>
Actual xml is like this. I am trying to rollup story pieces in "Related Stories' manner on the right side of a dynamic page.
<news>
<title>Story title</title>
<comments> story gist here..a couple of sentences.</comments>
<content> actualy story </content>
Please help. I found this resources by Marc andersen but the xsl is too complex for me filter through and make use of myself..
http://sympmarc.com/2010/07/13/displaying-the-first-n-words-of-announcement-bodies-with-xsl-in-a-dvwp/
Some help from the XSL gurus will be appreciated..
Here is a non-recursive, pure XSLT 1.0 solution:
<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="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vDigits" select="'0123456789'"/>
<xsl:variable name="vAlhanum" select="concat($vLower, $vUpper, $vDigits)"/>
<xsl:template match="comments">
<xsl:param name="pText" select="."/>
<xsl:choose>
<xsl:when test="not(string-length($pText) > 120)">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vTruncated" select="substring($pText, 1, 121)"/>
<xsl:variable name="vPunct"
select="translate($vTruncated, $vAlhanum, '')"/>
<xsl:for-each select=
"(document('')//node() | document('')//#* | document('')//namespace::*)
[not(position() > 121)]">
<xsl:variable name="vPos" select="122 - position()"/>
<xsl:variable name="vRemaining" select="substring($vTruncated, $vPos+1)"/>
<xsl:if test=
"contains($vPunct, substring($vTruncated, $vPos, 1))
and
contains($vAlhanum, substring($vTruncated, $vPos -1, 1))
and
string-length(translate($vRemaining, $vPunct, ''))
= string-length($vRemaining)
">
<xsl:value-of select="substring($vTruncated, 1, $vPos -1)"/>
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<news>
<title>Story title</title>
<comments> story gist here..a couple of sentences. Many more sentences ...
even some more text with lots of meaning and sense aaand a lot of humor. </comments>
<content> actualy story </content>
</news>
the wanted, correct result is produced:
story gist here..a couple of sentences. Many more sentences ...
even some more text with lots of meaning and sense
If have a big 'xsl:choose' chunk in which I need to set a number of defined sets of attributes on different elements.
I really do not like to repeat the definition of sets of attributes inside every branch of the 'choose'.
So I would like to work with a variable that contains those attributes.
A lot easier to maintain and less room for error...
So far I have not been able to call the attribute node out?
I thought they are just a node-set, so copy-of would do the trick.
But that gives me nothing on output.
Is this because attribute nodes are not really children?
But XSLT 1.O does not allow me to address them directly...<xsl:copy-of select="$attributes_body/#*/> returns an error
Here is the stylesheet fragment (reduced from original)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="list">
<xsl:for-each select="figure">
<xsl:variable name="attributes_body">
<xsl:attribute name="chapter"><xsl:value-of select="#chapter"/></xsl:attribute>
<xsl:attribute name="id"><xsl:value-of select="#id"/></xsl:attribute>
</xsl:variable>
<xsl:variable name="attributes_other">
<xsl:attribute name="chapter"><xsl:value-of select="#book"/></xsl:attribute>
<xsl:attribute name="id"><xsl:value-of select="#id"/></xsl:attribute>
</xsl:variable>
<xsl:choose>
<xsl:when test="ancestor::body">
<xsl:element name="entry">
<xsl:copy-of select="$attributes_body"/>
<xsl:text>Body fig</xsl:text>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="entry">
<xsl:copy-of select="$attributes_other"/>
<xsl:text>other fig</xsl:text>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
If this can not be done in XLST 1.0 would 2.0 be able to do this?
<xsl:variable name="attributes_body">
<xsl:attribute name="chapter"><xsl:value-of select="#chapter"/></xsl:attribute>
<xsl:attribute name="id"><xsl:value-of select="#id"/></xsl:attribute>
</xsl:variable>
You need to select the wanted attributes -- not to copy their contents in the body of the variable.
Remember: Whenever possible, try always to specify an XPath expression in the select attribute of xsl:variable -- avoid copying content in its body.
Solution:
Just use:
<xsl:variable name="attributes_body" select="#chapter | #id">
Here is a complete example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="x/a">
<xsl:variable name="vAttribs" select="#m | #n"/>
<newEntry>
<xsl:copy-of select="$vAttribs"/>
<xsl:value-of select="."/>
</newEntry>
</xsl:template>
</xsl:stylesheet>
when applied on this:
<x>
<a m="1" n="2" p="3">zzz</a>
</x>
produces:
<newEntry m="1" n="2">zzz</newEntry>
in my case, I was trying to store a tag attribute into a variable
to do so, use this syntax tag-name/#attribute-inside-tag-name
here is an example
<xsl:variable name="articleLanguage" select="/article/#language"/><!--the tricky part -->
//<!--now you can use this this varialbe as you like -->
<xsl:apply-templates select="front/article-meta/kwd-group[#language=$articleLanguage]"/>
and the xml was
<article article-type="research-article" language="es" explicit-lang="es" dtd-version="1.0">
.....
hope this help you