Given xsl file to change color
<xsl:if test="!colorChanges()">
<StaticLabel style="{StaticLabel/#style}">
<Caption>
<xsl:value-of select="$StaticLabel/Caption"/>
</Caption>
<PreviewCaption>
<xsl:value-of select="$StaticLabel/Caption"/>
</PreviewCaption>
</StaticLabel>
</xsl:if>
Given this xml data
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt">
<Caption><![CDATA[FoodType]]></Caption>
<Name><![CDATA[French]]></Name>
</StaticLabel>
</xsl:if>
Current result after xslt
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
Is there anyway to ONLY change the color while keeping other style attributes before xslt is performed without updating the xml file?
Expected result:
<StaticLabel style="font-family:Arial;color:#CCCCCC;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
Possible XSL solution
<xsl:if test="colorChanges()">
<StaticLabel>
<xsl:attribute name="style">
//other style attributes stay same and ONLY edit color
<xsl:text>color:#CCCCCC"</xsl:text>
</xsl:attribute>
<Caption>
<xsl:value-of select="$StaticLabel/Caption"/>
</Caption>
<PreviewCaption>
<xsl:value-of select="$StaticLabel/Caption"/>
</PreviewCaption>
</StaticLabel>
</xsl:if>
I. XSLT 1.0 solution:
This generic transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pStyle" select="'font-size:12pt'"/>
<xsl:variable name="vStyleName"
select="concat(';',substring-before($pStyle, ':'),':')"/>
<xsl:variable name="vCurrentStyleValue" select=
"substring-before(substring-after(concat(';', /*/#style, ';'), $vStyleName),
';')"/>
<xsl:variable name="vCurrentStyle"
select="concat($vStyleName,$vCurrentStyleValue)"/>
<xsl:template match="node()|#*" name="identiy">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/#style">
<xsl:attribute name="style">
<xsl:variable name="vprecStyles" select=
"substring-before(concat(';',., $vCurrentStyle), $vCurrentStyle)"/>
<xsl:value-of select="substring($vprecStyles, 2)"/>
<xsl:if test="$vprecStyles">;</xsl:if>
<xsl:value-of select="$pStyle"/>
<xsl:value-of select="substring-after(concat(';',.), $vCurrentStyle)"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
produces the wanted, correct result:
<StaticLabel style="font-family:Arial;color:#CCCCCC;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
When we change the $pStyle parameter to:
<xsl:param name="pStyle" select="'font-family:Courier'"/>
then again the wanted, correct result is produced:
<StaticLabel style="font-family:Courier;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
If we change the $pStyle parameter to:
<xsl:param name="pStyle" select="'font-size:12pt'"/>
We again get the correct result:
<StaticLabel style="font-family:Arial;color:#000000;font-size:12pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
Finally, if we change the $pStyle parameter to:
<xsl:param name="pStyle" select="'line-height:15pt'"/>
we again get the correct, desired result:
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt;line-height:15pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
This is quite error-proof -- observe:
<StaticLabel style="font-family:Arial;background-color:#ffffff;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
and we have the $pStyle parameter again as:
<xsl:param name="pStyle" select="'color:#CCCCCC'"/>
We still get the correct result (not confused by other CSS properties ending with "color"):
<StaticLabel style="font-family:Arial;background-color:#ffffff;color:#CCCCCC;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
II. XSLT 2.0 solution -- much easier:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pStyle" select="'color:#CCCCCC'"/>
<xsl:variable name="vStyleName" select=
"substring-before($pStyle, ':')"/>
<xsl:variable name="vcurStyles"
select="tokenize(/*/#style, ';')"/>
<xsl:variable name="vnewStyles" select=
"if($vcurStyles[substring-before(.,':') eq $vStyleName])
then ($vcurStyles[substring-before(.,':') ne $vStyleName],
$pStyle)
else ($vcurStyles, $pStyle)
"/>
<xsl:template match="node()|#*" name="identiy">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/#style">
<xsl:attribute name="style" select="string-join($vnewStyles, ';')">
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Related
In first template, I am intentionally excluding an element ('milk') because the parsed data map is relatively flat and I would like to use XSLT to categorize and structure the data. The aim is to process the excluded element ('milk') in the second template. The both templates works running them one at a time. Running the templates together will not show the result of the excluded element ('milk') which should set another attribute name and attribute value.
JSON:
<data>
{
"storage": {
"pencils": 12,
"milk": 8,
"rulers": 4
}
}
</data>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:storage="http://www.exammple.com/1"
xmlns:office="http://www.exammple.com/2"
xmlns:item="http://www.exammple.com/3"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<storage:one>
<xsl:apply-templates select="json-to-xml(.)"/>
</storage:one>
</xsl:template>
<!-- Print map -->
<!-- <xsl:template match="*[#key = 'storage']"> <xsl:copy-of select=".."/> </xsl:template> -->
<xsl:template match="*[#key='storage']">
<xsl:for-each select="*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="*[#key='milk']">
<xsl:for-each select=".">
<xsl:element name="item:{#key}">
<xsl:attribute name="beverage">plant-2</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
xmlns:office="http://www.exammple.com/2"
xmlns:storage="http://www.exammple.com/1">
<item:pencils office="plant-1">12</item:pencils>
<item:rulers office="plant-1">4</item:rulers>
</storage:one>
Wanted result:
<?xml version="1.0" encoding="UTF-8"?>
<storage:one xmlns:item="http://www.exammple.com/3"
xmlns:office="http://www.exammple.com/2"
xmlns:storage="http://www.exammple.com/1">
<item:pencils office="plant-1">12</item:pencils>
<item:rulers office="plant-1">4</item:rulers>
<item:milk beverage="plant-2">8</item:milk>
</storage:one>
Your second template is never matched, because it is never reached. All elements are processed by <xsl:template match="*[#key='storage']"> - which doesn't have an <xsl:apply-templates ...> to reach further templates.
Your first template does not recurse into its children. So add an <xsl:apply-templates select="*" /> to the end of the first template:
<xsl:template match="*[#key='storage']">
<xsl:for-each select="*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*" />
</xsl:template>
This will try to apply further templates at the level of "storage" and therefore match the second template.
I would write templates for each different output type and if the order of the output is different from the order of the input throw in an xsl:sort or an XPath 3.1 sort call to change the order:
<xsl:template match="data">
<storage:one>
<xsl:apply-templates select="json-to-xml(.)"/>
</storage:one>
</xsl:template>
<xsl:template match="*[#key = 'storage']">
<xsl:apply-templates select="sort(*, (), function($el) { $el/#key = 'milk' })"/>
</xsl:template>
<xsl:template match="*[#key='storage']/*[not(#key='milk')]">
<xsl:element name="item:{#key}">
<xsl:attribute name="office">plant-1</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="*[#key='storage']/*[#key='milk']">
<xsl:element name="item:{#key}">
<xsl:attribute name="beverage">plant-2</xsl:attribute>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
I have an xslt to convert xml to CSV. But I don't want records in output to be more than 100 characters. Below is my xslt, can some one help me to restrict each record of out put to 100 characters?
<?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" xmlns:wd="urn:com.workday.report/Positive_Pay_SVB"
xmlns:this="urn:this-stylesheet" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="text"/>
<xsl:variable name="delimiter" select="' '"/>
<xsl:variable name="linefeed" select="'
'"/>
<xsl:template match="wd:Report_Data">
<xsl:apply-templates select="wd:Report_Entry/wd:Payments_group"/>
<xsl:template match="wd:Report_Entry/wd:Payments_group">
<xsl:variable name="Paymentamount" select="translate(normalize-space(translate(wd:Payment_Total_Amount,'.','')),'','')"/>
<xsl:value-of select="wd:CF_Account_number_HSBC_Canada"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="concat(substring('0000000000', 1 + string-length(wd:Check_Number)), wd:Check_Number)"/>
<xsl:value-of select="concat(substring('0000000000', 1 + string-length($Paymentamount)), $Paymentamount)"/>
<xsl:value-of select="wd:Payment_Date"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="wd:Payment_Payee/#wd:Descriptor"/>
<xsl:value-of select='$linefeed'/>
</xsl:template>
</xsl:stylesheet>
You can always restrict the output length of a value with
substring(value,1,100)
If you have multiple xsl:value-of that you want to restrict, concatenate them and then apply the above expression:
substring(concat(value1,value2,...),1,100)
So applied to your template, this could look like this:
<xsl:template match="wd:Report_Entry/wd:Payments_group">
<xsl:variable name="Paymentamount" select="translate(normalize-space(translate(wd:Payment_Total_Amount,'.','')),'','')"/>
<xsl:value-of select="substring(wd:CF_Account_number_HSBC_Canada,1,100)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="substring(concat(substring('0000000000', 1 + string-length(wd:Check_Number)), wd:Check_Number, concat(substring('0000000000', 1 + string-length($Paymentamount)), $Paymentamount), wd:Payment_Date),1,100)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="substring(wd:Payment_Payee/#wd:Descriptor,1,100)"/>
<xsl:value-of select='$linefeed'/>
</xsl:template>
I am trying to perform simple transformation.
The error reported by the proccessor is: An attribute node (name) cannot be created after the children of the containing element. The error is pointing to this line <xsl:apply-templates select="#*|node()"/> into the last template.
EDIT#1:
This is the input:
<root>
<processor>../../library/saxon-he-9-6-0-7j/saxon9he.jar</processor>
<test-case name="test-case-1">
<object-under-test category="template" name="text-align"/>
<parameters>
<input name="text">text</input>
<input name="min-lenght">8</input>
<input name="align">left</input>
<output name="result"/>
</parameters>
<criteria>
<criterion class="equal" to="'text '"/>
</criteria>
</test-case>
</root>
This is the XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslAlt="dummy" version="1.0">
<xsl:namespace-alias stylesheet-prefix="xslAlt" result-prefix="xsl"/>
<!--~~~-->
<xsl:template match="root">
<xslAlt:stylesheet version="1.0">
<xslAlt:output method="xml"/>
<xslAlt:include href="../../../product/templates.xsl"/>
<xslAlt:template name="root" match="/">
<xsl:apply-templates select="test-case"/>
</xslAlt:template>
</xslAlt:stylesheet>
</xsl:template>
<!--~~-->
<xsl:template match="test-case">
<test-case name="{concat('test-case-', string(position()))}">
<xsl:variable name="test-case-return" select="concat('test-case-',string(position()),'-return')"/>
<xslAlt:variable name="{$test-case-return}">
<xslAlt:call-template name="{object-under-test/#name}">
<xsl:for-each select="parameters/input">
<xsl:choose>
<xsl:when test="string(number()) = 'NaN'">
<xslAlt:with-param name="{#name}" select="'{.}'"/>
</xsl:when>
<xsl:otherwise>
<xslAlt:with-param name="{#name}" select="{.}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xslAlt:call-template>
</xslAlt:variable>
<!--~~-->
<xslAlt:variable name="result" select="translate(${$test-case-return},{string('$space')},{string('$nbsp')})"/>
<xsl:apply-templates select="#*|node()"/>
</test-case>
</xsl:template>
<!--~~-->
<xsl:template match="criteria">
<criteria>
<xslAlt:variable name="test-result">
<xslAlt:choose>
<xslAlt:when test="{translate(criterion/#to,' ',' ')} = $result">TEST-PASSED</xslAlt:when>
<xslAlt:otherwise>TEST-FAILED</xslAlt:otherwise>
</xslAlt:choose>
</xslAlt:variable>
<xsl:apply-templates select="#*|node()"/>
</criteria>
</xsl:template>
<!--~~-->
<xsl:template match="#to">
<xsl:attribute name="to">
<xsl:value-of select="translate(.,' ',' ')"/>
<!--translate replace-->
</xsl:attribute>
<xsl:attribute name="result">{<xsl:value-of select="string('$test-result')"/></xsl:attribute>
</xsl:template>
<!--~~-->
<xsl:template match="parameters/output">
<output name="{#name}">
<xslAlt:value-of select="$result"/>
</output>
</xsl:template>
<!--~~-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/><!--THE ERROR IS POINTING HERE!!!-->
</xsl:copy>
</xsl:template>
<!--~~-->
</xsl:stylesheet>
The error reported by the proccessor is: An attribute node (name)
cannot be created after the children of the containing element.
This is not a "casting error". What it says is that you are trying to create an attribute after some children have already been created.
AFAICT, it happens here:
<xsl:template match="test-case">
<test-case name="{concat('test-case-', string(position()))}">
<!--here child nodes are being created!!! ~-->
<xsl:apply-templates select="#*|node()"/>
</test-case>
</xsl:template>
If you change (line #35):
<xsl:apply-templates select="#*|node()"/>
to:
<xsl:apply-templates select="node()"/>
the error will go away, because now it will stop trying to copy the existing attributes. You would not want this to happen anyway, since it would overwrite the name attribute you have created yourself.
Caveat:
I have not examined your stylesheet in-depth.
I have an xml coming as below
<Envelope>
<content>
<feild1>1</feild1>
....
<feild10>10<feild10>
</content>
</Envelope>
but want the xml to be (add the namespace prefix as xs1 and content lower case c to Upper case "C"
<Envelope>
<Content> <!-- Note the lower case c chnaged to Upper Case --->
<xs1:feild1>1</xs1:feild1> <!-- and for the feild, xs1 is added as prefix -->
....
<xs1:feild10>10</xs1:feild10>
</Content>
</Envelope>
Please help looking for XSLT 1.0
Here is my XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs1="https://temp">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="https://temp">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Hope this helps
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs1="https://temp">
<xsl:template match="/">
<xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<Envelope>
<xsl:for-each select="Envelope/*">
<xsl:variable name="root" select="local-name(.)"/>
<xsl:variable name="temp" select="translate($root,$smallcase,$uppercase)"/>
<xsl:variable name="temp2" select="concat(substring($temp,1,1),translate(substring($temp,2,string-length($temp)),$uppercase,$smallcase))"/>
<xsl:element name="{$temp2}">
<xsl:for-each select="child::*">
<xsl:element name="xs1:{local-name(.)}" namespace="https://temp">
<xsl:value-of select="node()"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</Envelope>
</xsl:template>
</xsl:stylesheet>
I have the following XML document which needs to be parsed with an XSLT to HTML.
<root>
<c>
<c1>
<id>1</id>
<text>US</text>
</c1>
<c1>
<id>2</id>
<text>UK</text>
</c1>
</c>
</root>
The XSLT for converting this to HTML is given below.
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" />
<xsl:template match="root">
<html>
<xsl:for-each select="c/c1">
**<xsl:variable name="vTemplate" select="text"/>
<xsl:apply-templates select="$vTemplate[#name='text'"/>**
</xsl:for-each>
</html>
</xsl:template>
<xsl:template match="xsl:template[#name='text']" name="text">
<select>
<xsl:attribute name="id">
<xsl:value-of select="id"/>
</xsl:attribute>
</select>
</xsl:template>
</xsl:stylesheet>
I need to call a template depends up on the text field. So for the value US, one template will be executed and for UK, another will executed.
How to achieve this with a variable as a template name while calling the template? I just made a try but it gives error. Can someone help me to figure out where i made wrong?
I think it is not possible to choose name of template to be called dynamically. What could be done is xsl:choose utilization (perhaps with combination with mode attribute), like this
<?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:template match="/root">
<html>
<xsl:for-each select="c/c1">
<xsl:choose>
<xsl:when test="text = 'US'">
<xsl:apply-templates select="text" mode="US"/>
</xsl:when>
<xsl:when test="text = 'UK'">
<xsl:apply-templates select="text" mode="UK"/>
</xsl:when>
<xsl:otherwise>
<xsl:comment>Something's wrong</xsl:comment>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</html>
</xsl:template>
<xsl:template match="text" mode="US">
<xsl:comment>US mode</xsl:comment>
<select>
<xsl:attribute name="id">
<xsl:value-of select="preceding-sibling::id"/>
</xsl:attribute>
</select>
</xsl:template>
<xsl:template match="text" mode="UK">
<xsl:comment>UK mode</xsl:comment>
<select>
<xsl:attribute name="id">
<xsl:value-of select="preceding-sibling::id"/>
</xsl:attribute>
</select>
</xsl:template>
</xsl:stylesheet>
Or you can use match with appropriate predicate and avoid for-each like this
<?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:template match="/root">
<html>
<xsl:apply-templates select="//c1" />
</html>
</xsl:template>
<xsl:template match="c1[text = 'US']">
<xsl:comment>US mode</xsl:comment>
<select id="{id}" />
</xsl:template>
<xsl:template match="c1[text = 'UK']">
<xsl:comment>UK mode</xsl:comment>
<select id="{id}" />
</xsl:template>
</xsl:stylesheet>
The id attribute of select can be also filled by "Attribute value templates" (xpath in curly brackets) as shown in previous sample.