Avoid passing parameters multiple times when calling xsl template repeatedly - xslt-1.0

I have a template that is being called with (not an exaggeration) roughly 100 parameters or so. I need to call this template several times as I need to alter the parent node(s) - sometimes it does not exist, sometimes there are several nodes, and there are several values that can be taken.
For example an example snippet of the current setup:
<xsl:choose>
<xsl:when test="$test='1'">
<body1>
<body3>
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="$previouslydefined1" />
.
.
.
<xsl:with-param name="param100" select="$previouslydefined100" />
</xsl:call-template>
<body3>
</body1>
</when>
<xsl:when test="$test='2'">
<body2>
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="$previouslydefined1" />
.
.
.
<xsl:with-param name="param100" select="$previouslydefined100" />
</xsl:call-template>
</body2>
</when>
<xsl:otherwise>
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="$previouslydefined1" />
.
.
.
<xsl:with-param name="param100" select="$previouslydefined100" />
</xsl:call-template>
</otherwise>
</xsl:choose>
I feel there must be a better way of doing this (probably several) without typing out a huge chunk of parameters repeatedly, but I'm drawing a blank.
I have two methods which I know don't work, but possibly there is a modified implementation that would.
Choose statement around the nodes individually (fails - bad syntax):
<xsl:choose>
<xsl:when test="$test='1'">
<body1>
</when>
<xsl:when test="$test='2'">
<body2>
</when>
<xsl:otherwise/>
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="$previouslydefined1" />
.
.
.
<xsl:with-param name="param1" select="$previouslydefined100" />
</xsl:call-template>
<xsl:choose>
<xsl:when test="$test='1'">
</body1>
</when>
<xsl:when test="$test='2'">
</body2>
</when>
<xsl:otherwise/>
</xsl:choose>
Use a value to set the node (fails - values can be empty in which case tag should not exist):
<xsl:element name="{$node}">
<xsl:element name="{$node2}">
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="$previouslydefined1" />
.
.
.
<xsl:with-param name="param1" select="$previouslydefined100" />
</xsl:call-template>
</xsl:element>
</xsl:element>
I also understand that it should be possible to use tunnelling (maybe) - I'm checking this possibility now.

Seems I messed up my original implementation of setting the nodes via a parameter. It is possible to leave the values empty in which case the nodes are not created which is what I was originally looking for. By using the maximum number of possible nodes necessary and then filling as necessary I can the template only once:
<xsl:element name="{$node}">
<xsl:element name="{$node2}">
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="$previouslydefined1" />
.
.
.
<xsl:with-param name="param1" select="$previouslydefined100" />
</xsl:call-template>
</xsl:element>
</xsl:element>

Related

xslt 1.0 Replace several characters [duplicate]

How do you replace an xml value, for example:
<name>Linda O'Connel</name>
to:
<name>Linda O''Connel</name>
via XSLT?
I need this because I have to pass this value in a powershell command line and to other platforms since the "double single quote" is needed to escape the apostrophe/single quotes.
Assuming an XSLT 1.0 processor, you will need to use a recursive named template for this, e.g:
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="searchString">'</xsl:param>
<xsl:param name="replaceString">''</xsl:param>
<xsl:choose>
<xsl:when test="contains($text,$searchString)">
<xsl:value-of select="substring-before($text,$searchString)"/>
<xsl:value-of select="$replaceString"/>
<!-- recursive call -->
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text,$searchString)"/>
<xsl:with-param name="searchString" select="$searchString"/>
<xsl:with-param name="replaceString" select="$replaceString"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of call:
<xsl:template match="name">
<xsl:copy>
<xsl:call-template name="replace">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
You can also try out the following.
<xsl:variable name="temp">'</xsl:variable>
<name>
<xsl:value-of select="concat(substring-before(name,$temp),$temp,$temp,substring-after(name,$temp))"/>
</name>

Duplicate Nodes with different input

Background info. I'm writing an XSL file to take an XML sent to the server by an analytical instrument and translate that file into an XML file accepted by our LIMS system.
Here is a Simple XML file coming from the instrument:
<dataRoot>
<dataRow>
<a0>2020-05-29 10:48:09 UTC-4</a0>
<a1>MSA - Conc.(Bench)</a1>
<a2>WHC202005270038</a2>
<a3>00251832</a3>
<a4>1.15966</a4>
<a5>MSA</a5>
<a6>101.067</a6>
</dataRow>
</dataRoot>
Here is what I have so far for the XSL File:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0"/>
<xsl:template match="/dataRoot">
<INBOUND>
<xsl:call-template name="A"/>
<xsl:copy-of select="A" />
</INBOUND>
</xsl:template>
<xsl:template match="/dataRow" name="A">
<xsl:for-each select="dataRow">
<xsl:variable name="sSampleID" select="normalize-space(a2)" />
<xsl:variable name="sSubmitter">AutoT_EQ00004</xsl:variable>
<xsl:variable name="sEnteredBy">EQ00004</xsl:variable>
<!-- Use a variable for OWNER, so that only one spot needs changing if a different value is desired -->
<xsl:variable name="sOwner">WHC</xsl:variable>
<xsl:variable name="sParameter" select="normalize-space(a5)" />
<xsl:variable name="sParamName"> <!-- Parameter names mapping for import to LIMS-->
<xsl:call-template name="ConvertPaName">
<xsl:with-param name="sPaName" select="$sParameter" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="sResult" select="normalize-space(a6)" />
<xsl:for-each select="$sParamName">
<INBOX_SAMPLE>
<EVENT>1</EVENT>
<SAMPLE_ID><xsl:value-of select="$sSampleID"/></SAMPLE_ID>
<SUBMITTER><xsl:value-of select="$sSubmitter" /></SUBMITTER>
<ENTERED_BY><xsl:value-of select="$sEnteredBy" /></ENTERED_BY>
<OWNER><xsl:value-of select="$sOwner" /></OWNER>
<PARAMETER_NAME><xsl:value-of select="$sParamName"/></PARAMETER_NAME>
<SRESULT><xsl:value-of select="$sResult"/></SRESULT>
</INBOX_SAMPLE>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<!-- Convert PaNames as needed, and skip processing certain values that are not actual Parameters (ie: 'Application') -->
<xsl:template name="ConvertPaName">
<xsl:param name="sPaName" />
<xsl:choose>
<xsl:when test="$sPaName='MSA'">MSA - Conc.(Bench)</xsl:when>
<xsl:when test="$sPaName='MSA'">Free MSA</xsl:when>
<xsl:otherwise>Halt_Import</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Here is the output of current translation:
<INBOUND>
<INBOX_SAMPLE>
<EVENT>1</EVENT>
<SAMPLE_ID>WHC202005270038</SAMPLE_ID>
<SUBMITTER>AutoT_EQ00004</SUBMITTER>
<ENTERED_BY>EQ00004</ENTERED_BY>
<OWNER>WHC</OWNER>
<PARAMETER_NAME>MSA - Conc.(Bench)</PARAMETER_NAME>
<SRESULT>101.067</SRESULT>
</INBOX_SAMPLE>
</INBOUND>
What I need to do is to duplicate this node for both parameter names:
<xsl:when test="$sPaName='MSA'">MSA - Conc.(Bench)</xsl:when>
<xsl:when test="$sPaName='MSA'">Free MSA</xsl:when>
So that my output becomes:
<INBOUND>
<INBOX_SAMPLE>
<EVENT>1</EVENT>
<SAMPLE_ID>WHC202005270038</SAMPLE_ID>
<SUBMITTER>AutoT_EQ00004</SUBMITTER>
<ENTERED_BY>EQ00004</ENTERED_BY>
<OWNER>WHC</OWNER>
<PARAMETER_NAME>MSA - Conc.(Bench)</PARAMETER_NAME>
<SRESULT>101.067</SRESULT>
</INBOX_SAMPLE>
<INBOX_SAMPLE>
<EVENT>1</EVENT>
<SAMPLE_ID>WHC202005270038</SAMPLE_ID>
<SUBMITTER>AutoT_EQ00004</SUBMITTER>
<ENTERED_BY>EQ00004</ENTERED_BY>
<OWNER>WHC</OWNER>
<PARAMETER_NAME>Free MSA</PARAMETER_NAME>
<SRESULT>101.067</SRESULT>
</INBOX_SAMPLE>
</INBOUND>
This is because instrument sends one parameter name like "MSA" but in our LIMS we have it under either "Free MSA" or "MSA - Conc.(Bench)" and I need to be able to provide the result for both possible parameter names and LIMS will insert the one that it find a match for.
Thank you so much in advanced :)
Add parm elements to template.
<xsl:template name="ConvertPaName">
<xsl:param name="sPaName" />
<xsl:choose>
<xsl:when test="$sPaName='MSA'">
<parm>MSA - Conc.(Bench)</parm>
<parm>Free MSA</parm>
</xsl:when>
<xsl:otherwise>
<parm>Halt_Import</parm>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Call your template
<xsl:variable name="sParamName">
<xsl:call-template name="ConvertPaName">
<xsl:with-param name="sPaName" select="$sParameter" />
</xsl:call-template>
</xsl:variable>
Convert to node set: (However you do it. You currently don't have the msxml namespace set up.)
<xsl:variable name="sParamNameList" select="msxml:node-set($sParamName)">
Then use the parm list in your for-each loop.
<xsl:for-each select="$sParamNameList/parm">

xslt string replace concrete example

I've found the template Codesling has provided as a substitute to the replace-function in XSLT 1.0: XSLT string replace
The problem I'm having is that I cannot figure out how to adapt it to my concrete example, so I'm hoping for some pointers here.
I've got a xml-parameter called ./bib-info that looks like this (for example):
<bib-info>Gäbler, René, 1971- [(DE-588)138691134]: Schnell-Umstieg auf Office 2010, [2010]</bib-info>
My goal is to replace the blanks between the "]:" and the beginning of the following text (in this case "Schnell"). The number of blanks is always the same btw.
Here are two other examples:
<bib-info>Decker, Karl-Heinz, 1948-2010 [(DE-588)141218622]: Funktion, Gestaltung und Berechnung, 1963</bib-info>
<bib-info>Decker, Karl-Heinz, 1948-2010 [(DE-588)141218622]: Maschinenelemente, 1963</bib-info>
Could anybody please give me a hint how to write the calling code
<xsl:variable name="newtext">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="replace" select="a" />
<xsl:with-param name="by" select="b" />
</xsl:call-template>
</xsl:variable>
so that it matches my problem?
Thanks in advance
Kate
My goal is to replace the blanks between the "]:" and the beginning of the following text
An example doing this is the following:
<xsl:template match="bib-info">
<xsl:variable name="newtext">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="text()" />
<xsl:with-param name="replace" select="']: '" />
<xsl:with-param name="by" select="']: '" />
</xsl:call-template>
</xsl:variable>
<bib-info><xsl:value-of select="$newtext" /></bib-info>
</xsl:template>
Note that this only replaces real spaces and not tabs or other special characters.
BTW replacing strings may not be the best choice in this case. You can achieve the same with the substring functions of XSLT-1.0:
<xsl:template match="bib-info">
<xsl:variable name="newtext1" select="substring-before(text(),']: ')" />
<xsl:variable name="newtext2" select="substring-after(text(),']: ')" />
<xsl:variable name="newtext" select="concat($newtext1,']: ',$newtext2)" />
<bib-info><xsl:value-of select="$newtext" /></bib-info>
</xsl:template>
The output is the same for both templates.

How to generate a cover image picture with asciidoctor-fopub

I'm trying to customize our developer guide pdf to have a decent looking first page. I'm totally fine with doing everything in photoshop as an image since asciidoc seems to be pretty limited in abilities here.
Unfortunately despite looking everywhere I can't seem to find a single way to customize the cover sheet or replace it with an image that works for asciidoctor-fopub.
The annoying thing is that this use case is specifically mentioned in the readme of the project https://github.com/asciidoctor/asciidoctor-fopub (under Custom XSL templates) but there is no mention on how to actually do it other than "its in the XSL". As someone who isn't familiar with docbook I have no idea where to begin.
asciidoctor-pdf has a tag for including an image but it barfs on our document and generates garbage (the ToC is painted over everything).
This can be done using division.xsl file under build/fopub/docbook/fo/
search for "Placeholder templates"
and paste below code there, you will be able to see cover image in your generated fopub
<!-- Placeholder templates -->
<xsl:template name="front.cover">
<xsl:call-template name="page.sequence">
<xsl:with-param name="master-reference">titlepage-cover-image</xsl:with-param>
<xsl:with-param name="content">
<fo:block text-align="center">
<fo:external-graphic src="url(path/to/cover.png)" content-height="250mm" content-width="176mm"/>
</fo:block>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Change height and width as per your book requirement.
Once that is done you need to provide margin for image on the cover page to be clear. Add below in pagesetup.xsl
<fo:simple-page-master master-name="titlepage-cover-image"
page-width="{$page.width}"
page-height="{$page.height}"
margin-top="{$title.page.margin.top}"
margin-bottom="{$page.margin.bottom}">
<xsl:attribute name="margin-{$direction.align.start}">
<xsl:value-of select="$page.margin.inner"/>
<xsl:if test="$fop.extensions != 0">
<xsl:value-of select="concat(' - (',$title.margin.left,')')"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="margin-{$direction.align.end}">
<xsl:value-of select="$page.margin.outer"/>
</xsl:attribute>
<xsl:if test="$axf.extensions != 0">
<xsl:call-template name="axf-page-master-properties">
<xsl:with-param name="page.master">titlepage-cover-image</xsl:with-param>
</xsl:call-template>
</xsl:if>
<fo:region-body margin-bottom="{$body.margin.bottom}"
margin-top="{$title.body.margin.top}"
column-gap="{$column.gap.titlepage}"
column-count="{$column.count.titlepage}">
<xsl:attribute name="margin-{$direction.align.start}">
<xsl:value-of select="$body.margin.inner"/>
</xsl:attribute>
<xsl:attribute name="margin-{$direction.align.end}">
<xsl:value-of select="$body.margin.outer"/>
</xsl:attribute>
</fo:region-body>
<fo:region-before region-name="xsl-region-before-first"
extent="{$region.before.extent}"
precedence="{$region.before.precedence}"
display-align="before"/>
<fo:region-after region-name="xsl-region-after-first"
extent="{$region.after.extent}"
precedence="{$region.after.precedence}"
display-align="after"/>
<xsl:call-template name="region.inner">
<xsl:with-param name="sequence">first</xsl:with-param>
<xsl:with-param name="pageclass">titlepage</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="region.outer">
<xsl:with-param name="sequence">first</xsl:with-param>
<xsl:with-param name="pageclass">titlepage</xsl:with-param>
</xsl:call-template>
</fo:simple-page-master>
Then add variables in fo-pdf.xsl to zeroed out your body top margin.
HTH.

Sanitizing DB inputs with XSLT

I've been looking for a method to strip my XML content of apostrophes (') since my DBMS is complaining of receiving those.
I need
<name> Jim O'Connor</name>
to become:
<name> Jim O''Connor</name>
By looking at the example described here, that is supposed to replace ' with '', I constructed the following script:
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template name="sqlApostrophe">
<xsl:param name="string" />
<xsl:variable name="apostrophe">'</xsl:variable>
<xsl:choose>
<xsl:when test="contains($string,$apostrophe)">
<xsl:value-of select="concat(substring-before($string,$apostrophe), $apostrophe,$apostrophe)"
disable-output-escaping="yes" />
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string"
select="substring-after($string,$apostrophe)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"
disable-output-escaping="yes" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
UPDATE: it works fine
Thanks for your help
The main problem is in your last template. As dacracot points out, xsl:apply-templates does not take a name attribute. To call a named template, you'd use xsl:call-template.
If you want to apply your SQL escaping to all text nodes, you could try replacing your last template with something like this:
<xsl:template match="text()">
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</xsl:template>
Also, why disable-output-escaping? If the text contains special characters (<, >, &), you'll get malformed XML as the output.
You have a couple of issues syntactically...
You can't have a name attribute on an apply-templates tag.
Your xpath, "node()|#*", is ambiguous.
Have you run this through a debugger? I would suggest Oxygen.