How use more than one value for attribute in XSLT - xslt

I want to use more than one value for the attribute in XSLT.
Input:
<contrib contrib-type="author">
<name>
<surname>Khorana</surname>
<given-names>Alok A.</given-names>
</name>
<degrees>MD</degrees>
</contrib>
<contrib contrib-type="author">
<name>
<surname>Holand</surname>
<given-names>Gamak J.</given-names>
</name>
<degrees>PhD</degrees>
</contrib>
The output should be:
<fieldSet name="Author" value="Alok A. Khorana, MD Gamak J. Holand, PhD"/>
Tried code:
<xsl:template name="take-author">
<tps:fieldSet name="Author">
<xsl:attribute name="value">
<xsl:value-of select="concat(descendant::contrib[#contrib-type='author']/name/given-names,descendant::contrib[#contrib-type='author']/name/surname)"/>
</xsl:attribute>
</tps:fieldSet>
</xsl:template>
But I am getting the following error when trying the above code
A sequence of more than one item is not allowed as the first argument
of concat()

The fact you get that message suggests you are using XSLT 2.0, which does not allow a sequence of more than one element as parameter to a function that expects a string argument.
If indeed you are using XSLT 2.0 (or above) you can do this...
<xsl:value-of select="descendant::contrib[#contrib-type='author']/concat(name/given-names, ' ', name/surname, ', ', degrees)" separator=" " />
(Note, the default separator is space anyway, so in this case you could omit the separator attribute, but I left it in just to demonstrate its use).
Also note, in XSLT 1.0, xsl:value-of will only return the value of the first node in a node-set, so you would have to write it like so:
<xsl:for-each select="descendant::contrib[#contrib-type='author']">
<xsl:if test="position() > 1"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of select="concat(name/given-names, ' ', name/surname, ', ', degrees)" />
</xsl:for-each>

Related

Select first space in a String

I want to select first space of a string in a text.
Input :
<p>Text 1<italic>should</italic> Text 2.</p>
There is a space after </italic>. I want to select only that space and replace a <s> for that space. How can i do that.
Tried code :
<xsl:template match="p/text()[2]">
<s/>
</xsl:template>
Expected results :
<p type="body">Text 1
<style type="underline">should</style><s/>have surgery.</p>
This tried code not works properly. I am using xslt 2.0
In XSLT 2.0, you could do:
<xsl:template match="p/text()[preceding-sibling::*[1][self::italic]]">
<xsl:analyze-string select="." regex="^\s" >
<xsl:matching-substring>
<s/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
This matches the text node immediately following an italic element, and checks if the first character is a space. If it is, it will be replaced by an empty s element.
Alternatively, you could restrict the match pattern itself to include only nodes that start with a space:
<xsl:template match="p/text()[preceding-sibling::*[1][self::italic]][starts-with(., ' ')]">
<s/>
<xsl:value-of select="substring(., 2)" />
</xsl:template>

How can I use processing instruction value in a different XSL template?

I am unable to use processing-instruction attribute values in a different template that is already used to add topic details in the header of the output file.
<task xml:lang="en-us" id="_01FDEB11">
<?ASTDOCREVINFO __docVerName="1.6" __docVerDesc="Description goes here" __docVerUser="Leroy" __docVerDate="Sep 25, 2017 10:44:44 AM"?>
I've created a template to pull values from a processing instruction but the variables don't keep the values in a different template.
<xsl:template match="processing-instruction('ASTDOCREVINFO')">
Version: <xsl:value-of select="substring-before(substring-after(., '__docVerName="'), '"')"/>
Date: <xsl:value-of select="substring-before(substring-after(., '__docVerDate="'), '"')"/>
<xsl:variable name="astVersion" select="substring-before(substring-after(., '__docVerName="'), '"')"/>
<xsl:variable name="astDate" select="substring-before(substring-after(., '__docVerDate="'), '"')"/>
Variable Version: <xsl:value-of select="$astVersion"/>
Variable Date: <xsl:value-of select="$astDate"/>
</xsl:template>
I'm unable to get this to work in a different template already used to pull topic info into the header of the output file.
<xsl:template
match="*[contains(#class, ' topic/topic ')][not(parent::*[contains(#class, ' topic/topic ')])]/*[contains(#class, ' topic/title ')]">
How can I add "processing-instruction('ASTDOCREVINFO')" to this template match?
You cannot pass information from one template match to another as XSLT is side effect free but in your second template you can use XPath to match the processing instruction which is a child of the root element. Something like:
<xsl:template
match="*[contains(#class, ' topic/topic ')][not(parent::*[contains(#class, ' topic/topic ')])]/*[contains(#class, ' topic/title ')]">
<!-- /* => means the root element of the XML document -->
<xsl:variable name="astoriaPI" select="/*/processing-instruction('ASTDOCREVINFO')"/>
<xsl:variable name="astVersion" select="substring-before(substring-after($astoriaPI, '__docVerName="'), '"')"/>
<xsl:variable name="astDate" select="substring-before(substring-after($astoriaPI, '__docVerDate="'), '"')"/>
</xsl:template>

substring XSLT over multiple value lines for an atttribute

I know the following xslt will work:
<xsl:attribute name="test">
<xsl:value-of select="substring(title, 1, 4000)"/>
</xsl:attribute>
But not sure what to do if there is something like the following and you want the substring over the whole attribute value not just the title or the substitle.
<xsl:attribute name="test">
<xsl:value-of select="title"/>
<xsl:if test="../../sub_title != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="../sub_title"/>
</xsl:if>
</xsl:attribute>
Is it even possible to apply a substring function over multiple lines that define an attribute?
I think what you are saying is that you want to build up a long string, consisting of the values of a number of other elements, and then truncate the result.
What you could do, is use the concat function to build the attribute value, and then do a substring on that.
<xsl:attribute name="test">
<xsl:value-of select="substring(concat(title, ' ', ../sub_title), 1, 4000)" />
</xsl:attribute>
In this case, if sub_title was empty, you would end up with a space at the end of the test attribute, so you might want to add a normalize-space to this expression
<xsl:value-of select="normalize-space(substring(concat(title, ' ', ../sub_title), 1, 4000))" />
An alternate approach, if you did want to use a more complicated expression, is to do the string calculation in a variable first
<xsl:variable name="test">
<xsl:value-of select="title"/>
<xsl:if test="../../sub_title != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="../sub_title"/>
</xsl:if>
</xsl:variable>
<xsl:attribute name="test">
<xsl:value-of select="substring($test, 1, 4000)" />
</xsl:attribute>
As an aside, you can simplify your code by using "Attribute Value Templates" here, instead of using the more verbose xsl:attribute command. Simply do this..
<myElement test="{substring($test, 1, 4000)}">
Here, the curly braces indicate an expression to be evaluated, rather than output literally.

Replacing specific character to lowercase using xsl

Hi i have an xml as below.
<setField identifier=”2”>
<fieldValues>
<fieldValue>
<field>event</field>
<value>
<boundVariable>$event1</boundVariable>
</value>
<type>java.lang.String</type>
</fieldValue>
</fieldValues>
<variable>append</variable>
</setField>
I need to convert to the following format.
<freeForm><text>append.setEvent($event1);</freeForm></text>
I am trying the following approach.
<xsl:template match="setField" name="setFieldTemplate">
<xsl:element name="freeForm">
<xsl:element name="text">
<xsl:value-of select="variable" />
<xsl:text>.set</xsl:text>
<xsl:value-of select="concat(translate(substring(field, 1, 1)"/>
<xsl:text>(</xsl:text>
<xsl:value-of select="boundVariable"/>
<xsl:text>);</xsl:text>
</xsl:element>
</xsl:element>
</xsl:template>
Here my requirment is based on the field name i need to generate corresponding statement as below.For that i need to change the first character of the field name to upper while generating to the following format.
append.setEvent($event1);
Here the field name is "event" and i need to generate setEvent(for which i am concatinating with the string "set").But i need to change the fieldname's first letter to upper one(Event from event).when i try with the above template with translate function i am facing some invalid xpath expression.
Please provide me some pointers to do the same.
<xsl:value-of select="concat(
variable,
'.set',
translate(substring(//field, 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'),
substring(//field, 2),
'(',
//boundVariable,
');'
)"/>
You want:
<xsl:value-of select="concat(
translate(substring(//field, 1, 1),
'abcdefghijklmnstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'),
substring(//field, 2))"/>
Also you might prefer:
<xsl:template match="setField">
<freeForm>
<text>
<xsl:value-of select="variable" />
<xsl:text>.set</xsl:text>
<xsl:value-of select="concat(
translate(
substring(fieldValues/fieldValue/field, 1, 1),
'abcdefghijklmnstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'),
substring(fieldValues/fieldValue/field, 2),
'(',fieldValues/fieldValue/value/boundVariable,');')"/>
</text>
</freeForm>
</xsl:template>

How do I render a comma delimited list using xsl:for-each

I am rendering a list of tickers to html via xslt and I would like for the list to be comma deliimited. Assuming I was going to use xsl:for-each...
<xsl:for-each select="/Tickers/Ticker">
<xsl:value-of select="TickerSymbol"/>,
</xsl:for-each>
What is the best way to get rid of the trailing comma? Is there something better than xsl:for-each?
<xsl:for-each select="/Tickers/Ticker">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:value-of select="TickerSymbol"/>
</xsl:for-each>
In XSLT 2.0 you could do it (without a for-each) using the string-join function:
<xsl:value-of select="string-join(/Tickers/Ticker, ',')"/>
In XSLT 1.0, another alternative to using xsl:for-each would be to use xsl:apply-templates
<xsl:template match="/">
<!-- Output first element without a preceding comma -->
<xsl:apply-templates select="/Tickers/Ticker[position()=1]" />
<!-- Output subsequent elements with a preceding comma -->
<xsl:apply-templates select="/Tickers/Ticker[position()>1]">
<xsl:with-param name="separator">,</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Ticker">
<xsl:param name="separator" />
<xsl:value-of select="$separator" /><xsl:value-of select="TickerSymbol" />
</xsl:template>
I know you said xsl 2.0 is not an option and it has been a long time since the question was asked, but for all those searching for a posibility to do what you wanted to achieve:
There is an easier way in xsl 2.0 or higher
<xsl:value-of separator=", " select="/Tickers/Ticker/TickerSymbol" />
This will read your /Tickers/Ticker elements and insert ', ' as separator where needed
If there is an easier way to do this I am looking forward for advice
Regards Kevin