Input XML is this:
<input>
<foo>John's bar</foo>
<bar>test</bar>
<foobar>testing</foobar>
</input>
After XSL transformation:
<input>
<foo>John's bar</foo>
<bar>this_test</bar>
</input>
But the legacy system expects:
<foo>John's bar</foo>
not <foo>John's bar</foo>
So I want to retain the value under <foo> as is rather than let XSLT parse it.
I tried using <xsl:output method="text"/> but with no luck of success..
I think XML itself when loaded gets parsed and XSLT just outputs as is..
If that's true I atleast want to escape it and make ' irrespective of whether it was &apose or ' in the input XML.
XSLT that I tried is this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bar">
<xsl:copy>
<xsl:text>this_</xsl:text>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="foobar"/>
</xsl:stylesheet>
If you are limited to XSLT 1.0, use disable-output-escaping="yes". This attribute can be used on xsl:text and xsl:value-of elements and it is deprecated in XSLT 2.0.
Stylesheet (XSLT 1.0)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vApos">'</xsl:variable>
<xsl:variable name="vAmp">&</xsl:variable>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bar">
<xsl:copy>
<xsl:text>this_</xsl:text>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="foobar"/>
<xsl:template match="foo">
<xsl:variable name="rep">
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select="$vApos" />
<xsl:with-param name="with" select="concat($vAmp,'apos;')"/>
</xsl:call-template>
</xsl:variable>
<xsl:copy>
<xsl:value-of select="$rep" disable-output-escaping="yes"/>
</xsl:copy>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$with"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The XSLT 1.0 solution makes use of advice given by Dimitre Novatchev here and Mads Hansen's answer here.
The XSLT 2.0 solution is more elegant, use a character-map to control the serialization of output. Make sure you escape the ampersand character as well (' instead of ').
Stylesheet (XSLT 2.0)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" use-character-maps="apo"/>
<xsl:strip-space elements="*"/>
<xsl:character-map name="apo">
<xsl:output-character character="'" string="'"/>
</xsl:character-map>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bar">
<xsl:copy>
<xsl:text>this_</xsl:text>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="foobar"/>
</xsl:stylesheet>
Output (Saxon 9.5 for 2.0, Xalan 2.7.1 for 1.0)
<?xml version="1.0" encoding="UTF-8"?>
<input>
<foo>John's bar</foo>
<bar>this_test</bar>
</input>
Related
Experts, i need to write XSLT 1.0 code to eliminate the Pipe delimited symbol inside double quotes and also need to remove those double quotes..
Input:
<?xml version="1.0" encoding="utf-8"?>
<ns:MT_FILE>
<LN>
<LD>EXTRACT|"28|53"|1308026.7500|1176</LD>
</LN>
<LN>
<LD>DETAIL|1176|"LOS LE|OS PARRILLA"|Y|R||||<LD>
</LN>
</ns:MT_FILE>
** Desired Output:**
<?xml version="1.0" encoding="utf-8"?>
<ns:MT_FILE>
<LN>
<LD>EXTRACT|2853|1308026.7500|1176</LD>
</LN>
<LN>
<LD>DETAIL|1176|LOS LE OS PARRILLA|Y|R||||<LD>
</LN>
</ns:MT_FILE>
** XSLT I used is below:**
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*/text()">
<xsl:value-of select="translate(., '\"', '')"/>
</xsl:template>
</xsl:stylesheet>
This XSLT removing all the double quotes from my input field, please assist here..
If it can be assumed that quotes will always come in pairs, you could do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="process">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="process">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '"')">
<xsl:value-of select="substring-before($text, '"')"/>
<xsl:value-of select="translate(substring-before(substring-after($text, '"'), '"'), '|', '')"/>
<xsl:call-template name="process">
<xsl:with-param name="text" select="substring-after(substring-after($text, '"'), '"')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As you tagged as EXSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:regexp="http://exslt.org/regular-expressions"
exclude-result-prefixes="regexp">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="LD/text()">
<xsl:value-of select="regexp:replace(., '(")([^|]+)\|([^"]+)(")', 'g', '$2$3')"/>
</xsl:template>
</xsl:stylesheet>
I have a file called ori.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<container>
<elA>
<el1>value1</el1>
<el2>value2</el2>
</elA>
<elB>
<el3>value3</el3>
<el4>value4</el4>
<el5>value5</el5>
</elB>
<elC>
<el6>value5</el6>
</elC>
</container>
</root>
and another one called modifs.xml:
<?xml version="1.0" encoding="UTF-8"?>
<els>
<el2>newvalue2</el2>
<el5>newvalue5</el5>
</els>
and I would like to obtain result.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<container>
<elA>
<el1>value1</el1>
<el2>newvalue2</el2>
</elA>
<elB>
<el3>value3</el3>
<el4>value4</el4>
<el5>newvalue5</el5>
</elB>
<elC>
<el6>value5</el6>
</elC>
</container>
</root>
I'm a beginner in XSLT.
So I started to write a stylesheet with which I'm able to change value2 into newvalue2:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="fileName" select="'modifs.xml'" />
<xsl:param name="modifs" select="document($fileName)" />
<xsl:param name="updateEl" >
<xsl:value-of select="$modifs/els/el2" />
</xsl:param>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//elA/el2">
<xsl:copy>
<xsl:apply-templates select="$updateEl" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But now I have to modify this stylesheet to be able to know which elements are in modifs.xml and find them in ori.xml. I don't know how to do that. Could you help please ?
I would use a key:
<xsl:key name="ref-change" match="els/*" use="local-name()"/>
<xsl:template match="*[key('ref-change', local-name(), $modifs)]">
<xsl:copy-of select="key('ref-change', local-name(), $modifs)"/>
</xsl:template>
However, using the third argument for the key function is only supported in XSLT 2 and later thus if you use an XSLT 1 processor you need to move the logic into the template, that requires using for-each to "switch" the context document
<xsl:template match="*">
<xsl:variable name="this" select="."/>
<xsl:for-each select="$modifs">
<xsl:choose>
<xsl:when test="key('ref-change', local-name($this))">
<xsl:copy-of select="key('ref-change', local-name($this))"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$this">
<xsl:call-template name="identity"/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
Put name="identity" on your identity transformation template.
I have an XML, something like this:
<?xml version="1.0" encoding="UTF-8"?>
<earth>
<computer>
<parts>;;remove;;This should stay;;remove too;;This stay;;yeah also remove;;this stay </parts>
</computer>
</earth>
I want to create an XSLT 2.0 transform to remove all text which starts and ends with ;;
<?xml version="1.0" encoding="utf-8"?>
<earth>
<computer>
<parts>This should stay This stay this stay </parts>
</computer>
</earth>
Try to do something like this but no luck:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="fn">
<xsl:output encoding="utf-8" method="xml" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="parts">
<xsl:element name="parts" >
<xsl:value-of select="replace(., ';;.*;;','')" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Wow, what a dumb way to markup text. You have XML at your disposal, why not use it? And even if marking this way, why not use different symbols for opening and closing the marked parts?
Anyway, I believe this returns the expected result:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="parts">
<xsl:copy>
<xsl:value-of select="replace(., ';;.+?;;', '')" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Another approach would be tokenize on ";;" as separator, then remove all even-numbered tokens:
<xsl:template match="parts">
<parts>
<xsl:value-of select="tokenize(.,';;')[position() mod 2 = 1]"
separator=""/>
</parts>
</xsl:template>
XSLT 1.0
For this kind of thing I'd use recursion. Just using string replace you can get what is before and after a certain character (or set of characters). All you need to do is continually loop over the string until there are no more occurrences of the replace character, like follows:
<xsl:template name="string-remove-between">
<xsl:param name="text" />
<xsl:param name="remove" />
<xsl:choose>
<xsl:when test="contains($text, $remove)">
<xsl:value-of select="substring-before($text,$remove)" />
<xsl:call-template name="string-remove-between">
<xsl:with-param name="text" select="substring-after(substring-after($text,$remove), $remove)" />
<xsl:with-param name="remove" select="$remove" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Then you'd just call the template with your text and the section you want to remove:
<xsl:call-template name="string-remove-between">
<xsl:with-param name="text" select="parts"/>
<xsl:with-param name="remove">;;</xsl:with-param>
</xsl:call-template>
Note that there are two substring-after calls, this makes sure we get the second instance of the replace characters ';;' so we aren't pulling in the text between.
Consider following xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<base>
<a>
<b>
<c>Text 1</c>
</b>
</a>
</base>
<base>
<a>
<b>
<c>Text 2</c>
</b>
</a>
</base>
</root>
and this xsl
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:value-of select="text()" />
<!-- i need Text 2 here -->
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
The original xml is much more nested an i don't know the exact structure. But there is a parallel node with the same structure. If my template is at //root/base[1]/a/b/c I want to reference //root/base[2]/a/b/c
However I only know that I am in some node below //root/base[1] and that there is the same node in //root/base[2].
Is there a possibility to accomplish this goal?
This stylesheet requires no extensions, and does not make any assumption on unique names; the boring part is to pass the correct "twin node" every time you call xsl:apply-templates:
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]">
<xsl:with-param name="twin" select="root/base[2]"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*">
<xsl:param name="twin"/>
<xsl:if test="text()">
<xsl:value-of select="text()" />
<xsl:value-of select="$twin/text()"/>
</xsl:if>
<xsl:for-each select="*">
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select=".">
<xsl:with-param name="twin" select="$twin/*[$pos]"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Solution with using saxon:evaluate extension (http://saxon.sourceforge.net/saxon7.9/extensions.html#evaluate)
You can also use dyn:evaluate if you are using xslt 1.0 processors (http://exslt.org/dyn/index.html):
<?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"
xmlns:saxon="http://saxon.sf.net/"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:variable name="path">
<xsl:call-template name="constructPath">
<xsl:with-param name="node" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rewritedPath" select="concat('/root/base[2]', substring($path, 11))"/>
<xsl:value-of select="text()" />
<xsl:value-of select="saxon:evaluate($rewritedPath)" />
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template name="constructPath">
<xsl:param name="node"/>
<xsl:choose>
<xsl:when test="$node/parent::node()/name()">
<xsl:call-template name="constructPath">
<xsl:with-param name="node" select="$node/parent::node()"/>
</xsl:call-template>
<xsl:value-of select="concat('/', $node/name())"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/', $node/name())"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Here we use recursive template "constructPath" to create string and process it in evaluate() function. This will select the exact node as in the parallel branch. Hope this will help.
Try the code below. This code will work only if each base element has children with unique element name.
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="text()" />
<xsl:value-of select="ancestor::base/following-sibling::base[1]//*[name() = $name]" />
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
However you can use dyn:evaluate from exslt extension or saxon:eval
My xsl file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:go="http://www.google.com"
exclude-result-prefixes="go">
<xsl:include href="SomeLibrary.xsl"/>
<xsl:template match="/">
<xsl:call-template name="SomeTemplate">
<xsl:with-param name="Element" select="'randomParam'"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
The SomeLibrary.xsl file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.google.com">
<xsl:template name="SomeTemplate">
<xsl:param name="Element"/>
<Blabla>
<xsl:value-of select="$Element" />
</Blabla>
</xsl:template>
</xsl:stylesheet>
Input xml: just use an empty XML. The result is this:
<?xml version="1.0" encoding="UTF-8"?>
<Blabla xmlns="http://www.google.com">
randomParam
</Blabla>
What I want is to have the "Blabla" node without a namespace. How can I remove it or make sure it doesn't get there, without modifying my "SomeLibrary.xsl"?
In case you don't want to edit the imported stylesheet code, the way to remove the namespace is with a two-pass transformation (which in XSLT 1.0 requires using an xxx:node-set() extension function):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:call-template name="SomeTemplate">
<xsl:with-param name="Element" select="'randomParam'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>
<xsl:apply-templates select="$vrtfPass1/node()" mode="pass2"/>
</xsl:template>
<xsl:template match="*" mode="pass2">
<xsl:element name="{name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="pass2"/>
</xsl:element>
</xsl:template>
<xsl:template name="SomeTemplate" xmlns="http://www.google.com">
<xsl:param name="Element"/>
<Blabla>
<xsl:value-of select="$Element" />
</Blabla>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted result (the default namespace removed) is produced:
<Blabla>randomParam</Blabla>
Update:
The OP has indicated in a comment that he is using Xalan 2.07.
Below is almost the same solution, but with namespace and name for the xxx:node-set() function, as used in Xalan:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="xmlns:xalan="http://xml.apache.org/xalan" exclude-result-prefixes="x" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:call-template name="SomeTemplate">
<xsl:with-param name="Element" select="'randomParam'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vPass1" select="x:nodeset($vrtfPass1)"/>
<xsl:apply-templates select="$vrtfPass1/node()" mode="pass2"/>
</xsl:template>
<xsl:template match="*" mode="pass2">
<xsl:element name="{name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="pass2"/>
</xsl:element>
</xsl:template>
<xsl:template name="SomeTemplate" xmlns="http://www.google.com">
<xsl:param name="Element"/>
<Blabla>
<xsl:value-of select="$Element" />
</Blabla>
</xsl:template>
</xsl:stylesheet>
Explicitly create them with:
<xsl:element name="Blabla" namespace="">...</xsl:element>
but this means fiddling with the SomeLibrary.xsl file.
add exclude-result-prefixes="go" to the stylesheet element.
and, of course, update the namespace declaration from the default one to xmlns:go="http://www.google.com"