xslt 1.0 Replace several characters [duplicate] - xslt-1.0

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>

Related

Find last translate

I have the following xml:
<item>value 1,value 2,value3,</item>
I would now like to achieve the following via an output via xslt:
<item>value 1,value 2,value3</item>
So remove the last comma. Can you help me?
Use this template:
<xsl:template match="item">
<xsl:copy>
<xsl:choose>
<xsl:when test="substring(.,string-length(.))=','">
<xsl:value-of select="substring(.,1,string-length(.)-1)"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
It will only remove the last char when it is a comma.

How do I use a variable in select in xsl

now
<xsl:for-each select="tm-reg-gaz/trademark/img">
want to change like this.
<xsl:variable name="tag_name">
<xsl:if test="tm-reg-gaz">tm-reg-gaz</xsl:if>
<xsl:if test="mp-tm-reg-gaz">mp-tm-reg-gaz</xsl:if>
</xsl:variable>
<xsl:for-each select="$tag_name/trademark/img">
</xsl:for-each>
I tried these
<xsl:for-each select="$tag_name/trademark/img">
<xsl:for-each select="path[$tag_name]/trademark/img">
<xsl:for-each select="{$tag_name}/trademark/img">
Let me know if it can. Thank you.
You can use select="*[name()=$tagname]/trademark/img".
A better approach is probably
<xsl:variable name="selection" select="tm-reg-gaz|mp-tm-reg-gaz">
<xsl:for-each select="$selection/trademark/img">
</xsl:for-each>
(But the details depend on whether both elements can appear or whether they are mutually exclusive).
Here is my proposal, feel free to shoot it down:
<xsl:variable name="variable" as="node()*">
<xsl:choose>
<xsl:when test="$testme = 'something'">
<xsl:copy-of select="//tm-reg-gaz/trademark/img"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="//mp-tm-reg-gaz/trademark/img"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:for-each select="$variable">
<!-- do something -->
</xsl:for-each>

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

XSLT1 error when get first occurence of a tag

This question (and my problem) is similar to XSLT1 Get the first occurence of a specific tag... But I have an "Undefined variable" error.
I have a XML with refpos attribute, that can be make by this first XSLT,
<xsl:template match="p[#class='ref']">
<xsl:copy>
<xsl:attribute name="refpos">
<xsl:value-of select="position()"/>
</xsl:attribute>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()" name="idt">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
Then, I a main (second) XSLT I have,
<xsl:variable name="firstRef">
<xsl:value-of select="(//p[#class='ref'])[1]/#refpos"/>
</xsl:variable>
<xsl:template match="p[#refpos=$firstRef]">
<title><xsl:apply-templates /></title>
</xsl:template>
<xsl:template match="#*|node()" name="idt">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
But here this XSLT not works!!
PS: I also believe that XSLT1 allow us to do everything in one step.
XSLT 1.0 does not allow variable references in template match expressions (XSLT 2.0 does). You'll have to move the check from the predicate inside the template:
<xsl:template match="p">
<xsl:choose>
<xsl:when test="#refpos=$firstRef">
<title><xsl:apply-templates /></title>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="idt" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>

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.