I am trying to use the Choose, When and Otherwise statement for a query I am writing for LDAP in CCure ( Access Control System). TEXT18 value should be "Inactive". Text12 value should be "False". Otherwise pulls the data from the LDAP attribute and creates another column name "Disable". Hope this makes sense and thank you.
<?xml version ="1.0" encoding="utf8" ?>
<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" />
<!-- Parameters assigned at runtime. -->
<xsl:param name="paramCurrentTimestamp">20001231173010</xsl:param>
<xsl:param name="paramCurrentDT" >12/31/2000 5:30:10 PM</xsl:param>
<xsl:param name="paramCurrentCulture">en-US</xsl:param>
<!-- The transformation below provides trivial default copy of everything. -->
<xsl:template match="*|#*">
<xsl:choose>
<xsl:when Text18=”Inactive”><xsl:value-of select=”Inactive”/>.</xsl:when>
<xsl:when Text12=”False”><xsl:value-of select=”False”/>.</xsl:when>
<xsl:otherwise> "ucscPersonEmployeeStatus[text()='']">
<xsl:element name="Disabled">1</xsl:element>
<xsl:copy-of select="." />
</xsl:template>
<xsl:template match="#*">
<xsl:copy />
</xsl:template>
<!-- End of customizable area. -->
</xsl:stylesheet>
You forgot to close the </xsl:otherwise> and the </xsl:choose> elements. Fixing these stylesheets issues, the following may work for you:
<?xml version ="1.0" encoding="utf8" ?>
<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" />
<!-- Parameters assigned at runtime. -->
<xsl:param name="paramCurrentTimestamp">20001231173010</xsl:param>
<xsl:param name="paramCurrentDT" >12/31/2000 5:30:10 PM</xsl:param>
<xsl:param name="paramCurrentCulture">en-US</xsl:param>
<!-- The transformation below provides trivial default copy of everything. -->
<xsl:template match="*|#*">
<xsl:choose>
<xsl:when Text18="Inactive">
<xsl:value-of select="Inactive"/>
.</xsl:when>
<xsl:when Text12="False">
<xsl:value-of select="False"/>
.</xsl:when>
<xsl:otherwise> "ucscPersonEmployeeStatus[text()='']">
<xsl:element name="Disabled">1</xsl:element>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*">
<xsl:copy />
</xsl:template>
<!-- End of customizable area. -->
</xsl:stylesheet>
You have two template rules that match attributes (#*) and that do different things. The first one (with match="*|#*") doesn't look as if it was designed to handle attributes.
When the entire body of a template rules consists of an xsl:choose, it's often better to split it into multiple template rules, for example
<xsl:template match="*[Text18='Inactive']">
<xsl:value-of select="`Inactive`"/>
</xsl:template>
<xsl:template match="*[Text12='False']">
<xsl:value-of select="`False`"/>
</xsl:template>
I'm guessing here that you want to output the literal strings "Inactive" and "False" rather than the content of elements named "Inactive" and "False", but with no information about your input and desired output, this can only be a guess.
Related
I'm having problems with resolving variables in XSLT. I have a working XSL file with fixed values that I now want to make dynamic (the variale declarations will be move outside the XSL-file once it works). My current problem is to use variable $beginning in the starts-with function. This is the way all the googling has lead me to believe it should look, but it will not compile. It works how I use it in the substring-after function. How should this be done?
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="oldRoot" select="'top'" />
<xsl:variable name="beginning" select="concat('$.',$oldRoot)" />
<xsl:variable name="newRoot" select="'newRoot'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bind/#ref[starts-with(., $beginning)]">
<xsl:attribute name="ref">
<xsl:text>$.newRoot.</xsl:text><xsl:value-of select="$oldRoot"></xsl:value-of>
<xsl:value-of select="substring-after(., $beginning)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
In XSLT 1.0 it is considered an error for a template match expression to contain a variable (See https://www.w3.org/TR/xslt#section-Defining-Template-Rules), so this line is failing
<xsl:template match="bind/#ref[starts-with(., $beginning)]">
(I believe some processors may allow it, but if they were following the spec, they shouldn't. It is allowed in XSLT 2.0 though).
What you can do is move the condition inside the template, and handle it with an xsl:choose
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="oldRoot" select="'top'" />
<xsl:variable name="beginning" select="concat('$.',$oldRoot)" />
<xsl:variable name="newRoot" select="'newRoot'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bind/#ref">
<xsl:choose>
<xsl:when test="starts-with(., $beginning)">
<xsl:attribute name="ref">
<xsl:text>$.newRoot.</xsl:text><xsl:value-of select="$oldRoot"></xsl:value-of>
<xsl:value-of select="substring-after(., $beginning)" />
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
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.
This may seem like an odd question as I did find a working solution, but can anyone tell me why my first xPath below works and the second one does not? I did include the enes namespace in my XSLT.
Solution A works:
<xsl:copy-of select="document('my_document_of_citations.xml')//node()[namespace-uri()='enes' and local-name()='section' and position() = $section-pos]/node()[namespace-uri()='enes' and local-name()='litref']" />
Solution B does not work:
<xsl:copy-of select="document('my_document_of_citations.xml')//enes:section[position() = $section-pos]/enes:litref" />
Here is the stylesheet, with only the code in a non-germain template and function omitted:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="enes thieme xhtml xlink xs" version="2.0" xmlns:enes="http://www.thieme.de/enes" xmlns:thieme="http://www.thieme.de/enes" xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:func="enesfunc">
<xsl:output method="xml" indent="yes" version="1.1" omit-xml-declaration="no" encoding="UTF-8" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<!-- Identity Transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="part[#type eq 'content']/section[#level eq '1']">
<xsl:element name="section" xmlns="enes">
<xsl:attribute name="level" select="1" />
<xsl:attribute name="id"><xsl:value-of select="./#id"/></xsl:attribute>
<xsl:attribute name="counter"><xsl:value-of select="./#counter"/></xsl:attribute>
<xsl:apply-templates />
<!-- Insert a level 2 section with the references for this level 1 section from references_by_chapter.xml here. -->
<xsl:element name="section" xmlns="enes">
<xsl:attribute name="level" select="2" />
<xsl:attribute name="type">
<xsl:text>literature</xsl:text>
</xsl:attribute>
<!-- Get the absolute position of this section within the document. -->
<xsl:variable name="section-pos" select="count(./preceding-sibling::section) + count(ancestor::node()/preceding-sibling::node()[local-name() eq 'part']/node()[local-name() eq 'section']) + 1" />
<!-- Copy extracted references from xml here -->
<xsl:copy-of select="document('references_by_chapter.xml')//node()[namespace-uri()='enes' and local-name()='section' and position() = $section-pos]/node()[namespace-uri()='enes' and local-name()='litref']" />
<!-- <xsl:copy-of select="document('references_by_chapter.xml')//enes:section[position() = $section-pos]/enes:litref" /> -->
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="section[#level eq '2']//text()">
<!-- code to transform level 2 sections here. -->
</xsl:template>
<xsl:function name="func:myStrFunc">
<!-- more code here. -->
</xsl:function>
Simplifying the code to remove irrelevant detail, consider
child::x[position()=2]
versus
child::*[name()='x' and position()=2]
The meaning of these two constructs is quite different. The first expression considers all the child elements whose name is 'x', and then returns the second of these. The second construct considers all the child elements, and then selects the second child provided that its name is 'x'.
Which of these "works" depends of course on what your requirements are. Both of them are correct, they just do different things.
I'm building a quite complex XSLT in order to generate some HTML markup.
On one my goal is to "extend" the class attribute of the generated markup using some templates.
Unfortunately, it does not works, because the XSLT tag <xsl:attribute> is only able to "set" attribute. Not to manipulate existing ones.
When I try, the original attribute is wiped.
Here is a small reproduction:
XML:
<node>
<item value="1" type="abc"/>
<item value="20" type="zxy"/>
</node>
XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="/node/item">
<p class="{#type}">
<xsl:call-template name='rule1' />
<xsl:call-template name='rule2' />
</p>
</xsl:template>
<xsl:template name='rule1'>
<xsl:attribute name='class'>
<xsl:choose>
<xsl:when test="#value mod 2 = 0">alpha</xsl:when>
<xsl:otherwise>omega</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
<xsl:template name='rule2'>
<xsl:attribute name='class'>
<xsl:choose>
<xsl:when test="#value mod 10 = 0">beta</xsl:when>
<xsl:otherwise>gamma</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
I would like to output:
<?xml version="1.0" encoding="utf-8"?>
<p class="abc omega"/>
<p class="zxy beta alpha"/>
But it outputs
<?xml version="1.0" encoding="utf-8"?>
<p class="omega"/>
<p class="beta"/>
is it possible to preserve the original attribute, or retrieve it within the utility template to reuse it?
It would seem that this answer would only satisfy a single deep, non-reiterated-upon class.
Include this at the start of the template you are calling:
<xsl:variable name="type" select="#type"/>
And then when it comes to updating the attribute value, try:
<xsl:attribute select="class">
<xsl:choose>
<xsl:when test="#value mod 2 = 0">
<xsl:value-of select="concat($type, ' alpha')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($type, ' omega')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
Again, I'm not super comfortable with XSLT but I did just try something similar in a test recently and it worked a charm. Hope it helps.
EDIT: I'm not sure but you might have to put the escaped value for a "space" in the concat function, I forget.
An approaching solution could be (thanks to #aleski suggestions)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="/node/item">
<p>
<xsl:attribute name='class'>
<xsl:value-of select="#type" />
<xsl:call-template name='rule1' />
<xsl:call-template name='rule2' />
</xsl:attribute>
</p>
</xsl:template>
<xsl:template name='rule1'>
<xsl:choose>
<xsl:when test="#value mod 2 = 0"><xsl:value-of select="' alpha'" /></xsl:when>
<xsl:otherwise><xsl:value-of select="' omega'" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name='rule2'>
<xsl:choose>
<xsl:when test="#value mod 10 = 0"><xsl:value-of select="' beta'" /></xsl:when>
<xsl:otherwise><xsl:value-of select="' gamma'" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Do you like obscure coding solutions? If so, you can re-write your solution as this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/node/item">
<p class="{#type} {substring('alphaomega', 1 + 5 * (#value mod 2 = 0), 5)} {normalize-space(substring('beta gamma', 1 + 5 * (#value mod 10 = 0), 5))}">
</p>
</xsl:template>
</xsl:stylesheet>
So, have three Attribute Value Templates in one attribute. If you take a look at one of them...
{substring('alphaomega', 1 + 5 * (#value mod 10 = 0), 5)}
This takes advantage of the fact that "true" evaluates to 1 in a numeric expression, and "false" evaluates to 0. So, when the expression is true, the first five characters of the string are returned. When it is false, the next five characters are.
How I can parse pipeline sign from fields in xslt. e.g.
dummy1|dummy2|dummy3|dummy4
Regards,
Sarah
If you use an XSLT 2.0 processor you can use the tokenize function (http://www.w3.org/TR/xpath-functions/#func-tokenize) e.g. with an input of
<foo>dummy1|dummy2|dummy3|dummy4</foo>
you can match
<xsl:template match="foo">
<xsl:value-of select="tokenize(., '\|')"/>
</xsl:template>
to output dummy1 dummy2 dummy3 dummy4. If you use an XSLT 1.0 processor you can check whether it supports an extension function like http://www.exslt.org/str/functions/tokenize/ or you need to write a recursive, named template splitting up the input.
In xlst 1.0 I usually use recursive call of named template, e.g.
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="dummyVar" select="'dummy1|dummy2|dummy3|dummy4'" />
<xsl:variable name="delimiter" select="'|'" />
<xsl:template match="/">
<dummies>
<xsl:call-template name="parseDummy">
<xsl:with-param name="parsedString" select="$dummyVar" />
</xsl:call-template>
</dummies>
</xsl:template>
<xsl:template name="parseDummy">
<xsl:param name="parsedString" />
<xsl:choose>
<xsl:when test="contains($parsedString, $delimiter)">
<xsl:element name="{substring-before($parsedString, $delimiter)}" />
<xsl:call-template name="parseDummy">
<xsl:with-param name="parsedString" select="substring-after($parsedString, $delimiter)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$parsedString}" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
It's on you what you need to do with parsed values, in example I construct elements with names of dummies.
Be careful about context which is changing.