Custom XSLT Tag - xslt-1.0

I am sick of write all the call-template/with-param stuff.
Is there any shortcut for this in example:
<xsl:call-template name="complexwidget">
<xsl:with-param name="a" select="$bla_213"/>
<xsl:with-param name="b" select="$bla_213"/>
<xsl:with-param name="c" select="$bla_213"/>
<xsl:with-param name="d" select="$bla_213"/>
</xsl:call-template>
A Perfect posibility would be this:
<complex a="{$bla_213}" b="{$bla_213}" c="{$bla_213}" d="{$bla_213}" />
Any idea, maybe a twice-transform-xslt??

If you could switch to xslt 2.0 then user functions would be exactly what you need.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:my="my-namespace">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="my:complexWidget(1, 2, 3, 4)" />
</xsl:template>
<xsl:function name="my:complexWidget">
<xsl:param name="a" />
<xsl:param name="b" />
<xsl:param name="c" />
<xsl:param name="d" />
<!-- Do something -->
<xsl:value-of select="($a, $b,$c, $d)" separator="-" />
</xsl:function>
</xsl:stylesheet>
In xslt 1.0 I think that preprocessing of xslt stylesheet with another transformation will be the only way (or may be writing some extension functions in language of your processor .
EDIT:
I tried the "preprocess" of xslt and it seems to be working (but probably it is no the most elegant way how to do it).
Input xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mypp="my-preprocess-namespace">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:variable name="varA" select="1"/>
<xsl:variable name="varB" select="2"/>
<xsl:variable name="varC" select="3"/>
<xsl:variable name="varD" select="4"/>
<mypp:complexWidget a="$varA" b="$varB" c="$varC" d="$varD"/>
</xsl:template>
<xsl:template name="complexWidget">
<xsl:param name="a"/>
<xsl:param name="b"/>
<xsl:param name="c"/>
<xsl:param name="d"/>
<!-- do something-->
<xsl:value-of select="$a"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="$b"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="$c"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="$d"/>
</xsl:template>
</xsl:stylesheet>
I defined here a special namespace for element to be replaced (i.e. xmlns:mypp="my-preprocess-namespace"). Element to be replaced is <mypp:complexWidget a="$varA" b="$varB" c="$varC" d="$varD"/>.
This xslt I preprocess with following xslt.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mypp="my-preprocess-namespace">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="mypp:*">
<xsl:element name="xsl:call-template">
<xsl:attribute name="name">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:for-each select="#*">
<xsl:element name="xsl:with-param">
<xsl:attribute name="name">
<xsl:value-of select="name()" />
</xsl:attribute>
<xsl:attribute name="select">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It is based on "Identity transform" - copy everything to the output just elements from special namespace transform into the call-template element. I guess this could be done in more nice way than I did.
The output is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:mypp="my-preprocess-namespace" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:variable name="varA" select="1"/>
<xsl:variable name="varB" select="2"/>
<xsl:variable name="varC" select="3"/>
<xsl:variable name="varD" select="4"/>
<xsl:call-template name="complexWidget">
<xsl:with-param name="a" select="$varA"/>
<xsl:with-param name="b" select="$varB"/>
<xsl:with-param name="c" select="$varC"/>
<xsl:with-param name="d" select="$varD"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="complexWidget">
<xsl:param name="a"/>
<xsl:param name="b"/>
<xsl:param name="c"/>
<xsl:param name="d"/>
<!-- do something-->
<xsl:value-of select="$a"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="$b"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="$c"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="$d"/>
</xsl:template>
</xsl:stylesheet>
When I run the output as xslt stylesheet it produces expected output.
But I don't have any deeper experience with producing xslt stylesheet by another xslt so I don't know much about possible troubles with this approach.

Related

Apply templates that matchs two conditions

I need to list all the INST names but only if the "onlyTesters" node don´t exists in the "inst/idef" part of XML body above.
I know thats strange but I can´t change the XML I receive.
XML:
<river>
<station num="699">
<inst name="FLU(m)" num="1">
<idef></idef>
</inst>
<inst name="Battery(V)" num="18">
<idef>
<onlyTesters/>
</idef>
</inst>
</station>
<INST name="PLU(mm)" num="0" hasData="1" virtual="0"/>
<INST name="FLU(m)" num="1" hasData="1" virtual="0"/>
<INST name="Q(m3/s)" num="3" hasData="1" virtual="1"/>
<INST name="Battery(V)" num="18" hasData="1" virtual="0"/>
</river>
XSL:
<xsl:template match="/">
<xsl:apply-templates select="//INST[#hasData = 1 and not(//inst[#num=(current()/#num)]/idef/onlyTesters)]/#name"/>
</xsl:template>
<xsl:template match="//INST[#hasData = 1 and not(//inst[#num=(current()/#num)]/idef/onlyTesters)]/#name">
<xsl:value-of select="#name"/>,
</xsl:template>
I´m having no match.
This is the result I expect:
PLU(mm),FLU(m),Q(m3/s)
You can achieve this with only one template:
<xsl:template match="/">
<xsl:for-each select="//INST[#hasData='1' and not(#name=//inst[idef/onlyTesters]/#name)]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
Output is:
PLU(mm), FLU(m), Q(m3/s)
Cross-references are best resolved using a key - for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:key name="inst" match="inst" use="#name" />
<xsl:template match="/river">
<xsl:for-each select="INST[#hasData = 1 and not(key('inst', #name)/idef/onlyTesters)]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Or even simpler:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:key name="exclude" match="onlyTesters" use="ancestor::inst/#name" />
<xsl:template match="/river">
<xsl:for-each select="INST[#hasData = 1 and not(key('exclude', #name))]">
<xsl:value-of select="#name"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Dynamically update text nodes in XSLT

I'm trying to update the text nodes in xml based on the check if it matches a certain pattern
in xslt 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:regexp="http://exslt.org/regular-expressions">
<xsl:output method="xml" version="1.0"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:copy>
<xsl:call-template name="CheckAndReplace">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="pattern" select=""/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="CheckAndReplace">
<xsl:param name="text"/>
<xsl:param name="pattern"/>
<xsl:for-each select="regexp:match( $text, $pattern, 'gi' )">
<xsl:copy-of select="regexp:replace( $text, $pattern, 'gi','*' )"
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XML:
<Root>
<Name> Kabir </Name>
<Id>  </Id>
</Root>
Here the request has ID tag which matches my pattern and that needs to be replaced
Result Required:
<Root>
<Name> Kabir </Name>
<Id> * </Id>
</Root>
There is no regex in XSLT 1.0. If your goal is to replace all occurrences of the  character with a * then try:
<xsl:template match="text()">
<xsl:value-of select="translate(., '', '*')" />
</xsl:template>

xslt nested transformation : unable to transnform

I am learning XSLT and trying to transform below xml. But couldn't achieve the task
<sample id="7">
<land1 id="8">
<owner>TOMMY</owner>
<type>INDIVIDUAL</type>
<hint>TOM_INDIVIDUAL</hint>
<date>12.02.2014</date>
<text>land details</text>
<number>1</number>
<cost>WIDERRUFLICH</cost>
</land1>
</sample>
and trying to convert above into
<table name="sample">
<tablename="land1">
<rel name="owner" value="TOMMY"/>
<rel name="type" value="INDIVIDUAL"/>
<rel name="<hint" value="TOM_INDIVIDUAL"/>
<rel name="date" value="12.02.2014"/>
<rel name="details" value="land details"/>
<rel name="number" value="1"/>
<rel name="cost" value="25%"/>
</table>
</table>
I tried below to generate the same, but it's not working.
<?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="/">
<xsl:for-each select="*">
<xsl:if test="current().count(*)>0">
<xsl:element name="table">
<xsl:attribute name="name">
<xsl:value-of select="name(.)"/>
</xsl:attribute>
<xsl:apply-templates select="/"
</xsl:element>
</xsl:if>
<xsl:if test="current().count(*)=0">
<xsl:element name="rel">
<xsl:attribute name="name">
<xsl:value-of select="name(.)"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="current()"/>
</xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Can someone please let me know where I am doing wrong?
Can someone please let me know where I am doing wrong?
Well, for one thing, current().count(*)>0 is not a valid expression.
And you have <xsl:apply-templates select="/" without closing the tag. Which may be a good thing - because if it worked, it would have created an infinite loop.
I also don't understand the overall logic of your approach. Couldn't you do simply:
<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="*"/>
<xsl:template match="*[*]">
<table name="{name()}">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="*">
<rel name="{name()}" value="{.}"/>
</xsl:template>
</xsl:stylesheet>
Added:
every element with an attribute should also be a table. For example,
in the above xml, <land1 id="8"> has children and your logic works
fine. But my inout can also contain <land1 id="8"/>. Now even though
it doesn't have any child elements, still element name has to be
considered as Table
Then use:
<xsl:template match="*[#*]">
instead of:
<xsl:template match="*[*]">

How to Parse Query parameters in XSLT

I have a requirement I will get the URL in the format like this below
https://sample.com?first=one&second=two&third=three
How can I form an xml structure below which will be formed by making use of this query parameters
<first>one</first><second>two</second><third>three</third>
can Please somebody help me with thisand provide me an xslt for this requirement
Given the following XML input (note the escaping of the ampersand character):
<URL>https://sample.com?first=one&second=two&third=three</URL>
the folowing stylesheet:
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:template match="/">
<output>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after(URL, '?')"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'&'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<xsl:element name="{substring-before($token, '=')}">
<xsl:value-of select="substring-after($token, '=')"/>
</xsl:element>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<first>one</first>
<second>two</second>
<third>three</third>
</output>
Note that this will work only if the query field names are also valid XML element names.

Iterate through xml nodes within XSLT

With in an xslt 1.0. I am calling a java function getTierByBundleId which returns below xml. I want to iterate through each Tier using for each. How can i do that
<xsl:variable name="varTierList" select="testclass:getTierByBundleId($bid,$ApplicationId)"/>
Xml returned by function getTierByBundleId
<TierList>
<Tier>
<TierId>1</TierId>
<Name>test</Name>
<Type>2</Type>
<Price>10</Price>
</Tier>
<Tier>
<TierId>2</TierId>
<Name>test</Name>
<Type>3</Type>
<Price>11</Price>
</Tier>
</TierList>
Here when I am trying to do xsl:for each on the variable which has the xml I am getting compilation error. How can I access each Tier from variable $varTierList
<xsl:for-each select="$varTierList/TierList/Tier">
<TierId><xsl:for each"Tierid"/>
</xsl:for-each
Below is the xslt which is generation the above TierList xml
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:foxtelclass="test.GetOrderQuote" exclude-result-prefixes="foxtelclass" extension-element-prefixes="testclass">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:if test="count(/ROWSET/ROW) > 0">
<TierList>
<xsl:for-each select="/ROWSET/ROW">
<Tier>
<TierId><xsl:value-of select="TIER_ID" /></TierId>
<Name><xsl:value-of select="NAME" /></Name>
<Type><xsl:value-of select="TIER_TYPE" /></Type>
<Price><xsl:value-of select="PRICE" /></Price>
</Tier>
</xsl:for-each>
</TierList>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
<xsl:for-each select="$varTierList/TierList/Tier"> is throwing the error message
Here is the full xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testclass="test.GetOrderQuote"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="testclass" extension-
element-prefixes="testclass">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="ApplicationId"/>
<xsl:template match="/">
<ServiceList>
<xsl:variable name="unique-list" select="//ROWSET/ROW/SERVICE_ID
[not(.=following::SERVICE_ID)]"/>
<xsl:for-each select="$unique-list">
<xsl:if test=".!=0">
<Service>
<xsl:variable name="Sid" select="."/>
<ServiceInternalId>
<xsl:value-of select="."/>
</ServiceInternalId>
<BundleList>
<xsl:variable name="unique-
bundle" select="//SERVICE_ID/../BUNDLE_ID[not(.=following::BUNDLE_ID)]"/>
<xsl:for-each
select="$unique-bundle">
<xsl:variable
name="bid" select="."/>
<xsl:if
test="count(/ROWSET/ROW/SERVICE_ID[text()=$Sid]/../BUNDLE_ID[text()=$bid])> 0">
<Bundle>
<Bundle_Id>
<xsl:value-of select="$bid"/>
</Bundle_Id>
<Type>
<xsl:value-of select="../BUNDLE_TYPE"/>
</Type>
<Name>
<xsl:value-of select="../BUNDLE_NAME"/>
</Name>
<Price>
<xsl:value-of select="../BUNDLE_PRICE"/>
</Price>
<xsl:variable name="varTierList" select="testclass:getTierByBundleId
($bid,$ApplicationId)"/>
<test>
<xsl:value-of select="$varTierList"/>
</test>
<xsl:for-each select="$varTierList/TierList/Tier"><!--line causing error-->
<TierId>
<xsl:value-of select="TierId"/>
</TierId>
</xsl:for-each>
</Bundle>
</xsl:if>
</xsl:for-each>
</BundleList>
</Service>
</xsl:if>
</xsl:for-each>
</ServiceList>
</xsl:template>
</xsl:stylesheet>
Below is the text from test node which what is xml variable varTierList contains
<test><TierList>
<Tier>
<TierId>109</TierId>
<Name>Boxes</Name>
<Type>10</Type>
<Price>10</Price>
</Tier>
</TierList>
</test>
Please find below input xml
<ROWSET>
<ROW>
<SET_ID>0</SET_ID>
<SET_DATE>2014-11-09 00:00:00.0</SET_DATE>
<BUNDLE_NAME>Test</BUNDLE_NAME>
<BUNDLE_ID>131</BUNDLE_ID>
<BUNDLE_PRICE>30</BUNDLE_PRICE>
<BUNDLE_TYPE>3</BUNDLE_TYPE>
<BUNDLECOMPONENT_LIST>101100</BUNDLECOMPONENT_LIST>
<PACKAGE>10015</PACKAGE>
<PACKAGE_TYPE>5</PACKAGE_TYPE>
<COMPONENT>101100</COMPONENT>
<PRODUCT_DESC>World Movies</PRODUCT_DESC>
<RATE_AMOUNT>10</RATE_AMOUNT>
<DISCOUNT_AMOUNT>0</DISCOUNT_AMOUNT>
<SERVICE_ID>98683812</SERVICE_ID>
<CHARGE_TYPE>RC</CHARGE_TYPE>
<GUID_TOKEN>053944D794856E3FE0540010E00D30B8</GUID_TOKEN>
<NRC_LINE_ID>0</NRC_LINE_ID>
<TIERID>11</TIERID>
</ROW>
<ROW>
<SET_ID>0</SET_ID>
<SET_DATE>2014-11-09 00:00:00.0</SET_DATE>
<BUNDLE_NAME>Optional test</BUNDLE_NAME>
<BUNDLE_ID>131</BUNDLE_ID>
<BUNDLE_PRICE>30</BUNDLE_PRICE>
<BUNDLE_TYPE>3</BUNDLE_TYPE>
<BUNDLECOMPONENT_LIST>101100</BUNDLECOMPONENT_LIST>
<PACKAGE>10015</PACKAGE>
<PACKAGE_TYPE>5</PACKAGE_TYPE>
<COMPONENT>101103</COMPONENT>
<PRODUCT_DESC>RAI International</PRODUCT_DESC>
<RATE_AMOUNT>20</RATE_AMOUNT>
<DISCOUNT_AMOUNT>0</DISCOUNT_AMOUNT>
<SERVICE_ID>98683812</SERVICE_ID>
<CHARGE_TYPE>RC</CHARGE_TYPE>
<GUID_TOKEN>053944D794856E3FE0540010E00D30B8</GUID_TOKEN>
<NRC_LINE_ID>0</NRC_LINE_ID>
<TIERID>14</TIERID>
</ROW>
</ROWSET>
Hope this helps,
this is full xslt that doesn't shows any compilation error at my end
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:testclass="test.GetOrderQuote" xmlns:ext="http://exslt.org/common" exclude-result-prefixes="testclass">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="ApplicationId"/>
<xsl:template match="/">
<ServiceList>
<xsl:variable name="unique-list" select="//ROWSET/ROW/SERVICE_ID[not(.=following::SERVICE_ID)]"/>
<xsl:for-each select="$unique-list">
<xsl:if test=".!=0">
<Service>
<xsl:variable name="Sid" select="."/>
<ServiceInternalId>
<xsl:value-of select="."/>
</ServiceInternalId>
<BundleList>
<xsl:variable name="unique-bundle" select="//SERVICE_ID/../BUNDLE_ID[not(.=following::BUNDLE_ID)]"/>
<xsl:for-each select="$unique-bundle">
<xsl:variable name="bid" select="."/>
<xsl:if test="count(/ROWSET/ROW/SERVICE_ID[text()=$Sid]/../BUNDLE_ID[text()=$bid])> 0">
<Bundle>
<Bundle_Id>
<xsl:value-of select="$bid"/>
</Bundle_Id>
<Type>
<xsl:value-of select="../BUNDLE_TYPE"/>
</Type>
<Name>
<xsl:value-of select="../BUNDLE_NAME"/>
</Name>
<Price>
<xsl:value-of select="../BUNDLE_PRICE"/>
</Price>
<xsl:variable name="varTierList" select="testclass:getTierByBundleId($bid,$ApplicationId)"/>
<test>
<xsl:value-of select="$varTierList"/>
</test>
<xsl:for-each select="$varTierList/TierList/Tier">
<TierId>
<xsl:value-of select="TierId"/>
</TierId>
</xsl:for-each>
</Bundle>
</xsl:if>
</xsl:for-each>
</BundleList>
</Service>
</xsl:if>
</xsl:for-each>
</ServiceList>
</xsl:template>
</xsl:stylesheet>