Pass in reg ex as variable - xslt-2.0

I would like to pass in a regular express pattern through a variable i.e. as below:
<?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:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="pattern" as="xs:string" select ="'[0-9]{4}'"/>
<xsl:variable name="caseSeries" >
<xsl:analyze-string select="'asdasd1980asdasd'" regex="$pattern">
<xsl:matching-substring>
<xsl:value-of select="."/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<result>
<areaCode>
<xsl:value-of select="$caseSeries"/>
</areaCode>
</result>
</xsl:template>
</xsl:stylesheet>
but it's not matching anything, I tried escaping the curly bracket but it doesn't work. I'm planning on having an if statement to get the appropriate regex into a variable and apply that regex, so this is a POC in a way.

adding curly braces for the variable does the trick:
<xsl:analyze-string select="'asdasd1980asdasd'" regex="{$pattern}">
I updated the link to work

Related

tokenize with delimeter inside and outside of a string with xslt 2.0

I do have an input with randon values in parentheses,square brackets,Curly brackets and values outside brackets.Any type of bracket can occur in any randam position, where all are seperated by delimeter comma.
I have used <xsl:for-each select="tokenize(test,',')">
but as comma is present both inside and outside of brackets. It became impossible to achieve desired output. Please help me out
for example
INPUT
<test>{ST456,PT154},[GH456,JH768],(HJ789,KY456),GH789,PI345</test>
Desired OUTPUT
<test>{ST456,PT154}</test>
<test>[GH456,JH768]</test>
<test>(HJ789,KY456)</test>
<test>GH789</test>
<test>PI345</test>
You can use the xsl:analyze-string element in XSLT 2 or in XSLT 3 the same element or instead the analyze-string function, as in
<xsl:stylesheet 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"
expand-text="yes"
exclude-result-prefixes="xs fn"
version="3.0">
<xsl:param name="regex-pattern" as="xs:string" expand-text="no">\[([^\]]+)\]|\{([^\}]+)\}|\(([^)]+)\)|([^,]+)</xsl:param>
<xsl:output indent="yes"/>
<xsl:template match="test">
<xsl:apply-templates select="analyze-string(., $regex-pattern)//fn:group"/>
</xsl:template>
<xsl:template match="fn:group">
<test>{.}</test>
</xsl:template>
</xsl:stylesheet>
Online sample is at https://xsltfiddle.liberty-development.net/6qVRKw9, for XSLT 2 you would simply use an xsl:analyze-string element with the same pattern and then remove leading or trailing braces:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
expand-text="yes"
exclude-result-prefixes="xs"
version="2.0">
<xsl:param name="regex-pattern" as="xs:string" expand-text="no">\[([^\]]+)\]|\{([^\}]+)\}|\(([^)]+)\)|([^,]+)</xsl:param>
<xsl:output indent="yes"/>
<xsl:template match="test">
<xsl:analyze-string select="." regex="{$regex-pattern}">
<xsl:matching-substring>
<test>
<xsl:value-of select="replace(., '^[\[\{\(]|[\}\]\)]$', '')"/>
</test>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qVRKw9/1

How to display latest date from N number of month in xslt?

I need to display latest date in N number of months using xslt.
My input:
2016/10/18
2016//10/15
2016/09/29
2016/09/15
and so on.
My output should be like below:
2016/10/18
2016/09/29
Can anyone help me on this?
Given a string of dates in that format you first need to tokenize to extract the date values, then you need to convert to the xs:date format, then you can group by the month and select the maximum value in each group. Using XSLT 3.0 that can be done as follows:
<?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:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:param name="input" as="xs:string">2016/10/18 2016/10/15 2016/09/29 2016/09/15</xsl:param>
<xsl:variable name="dates" as="xs:date*"
select="tokenize($input, '\s+')!xs:date(replace(., '/', '-'))"/>
<xsl:variable name="max-dates" as="xs:date*">
<xsl:for-each-group select="$dates" group-by="month-from-date(.)">
<xsl:sort select="current-grouping-key()"/>
<xsl:sequence select="max(current-group())"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:template name="main" match="/">
<xsl:value-of select="$max-dates" separator="
"/>
</xsl:template>
</xsl:stylesheet>
In XSLT 2.0 you need to rewrite the date sequence construction a bit:
<?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:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="2.0">
<xsl:param name="input" as="xs:string">2016/10/18 2016/10/15 2016/09/29 2016/09/15</xsl:param>
<xsl:variable name="dates" as="xs:date*"
select="for $dateString in tokenize($input, '\s+') return xs:date(replace($dateString, '/', '-'))"/>
<xsl:variable name="max-dates" as="xs:date*">
<xsl:for-each-group select="$dates" group-by="month-from-date(.)">
<xsl:sort select="current-grouping-key()"/>
<xsl:sequence select="max(current-group())"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:template name="main" match="/">
<xsl:value-of select="$max-dates" separator="
"/>
</xsl:template>
</xsl:stylesheet>
I. Here is a short XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:for-each-group select="d" group-by="substring(.,6,2)">
<xsl:sequence select="current-group()[. eq max(current-group()/string())][1]"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (unordered and multi-year dates -- to make it more interesting):
<t>
<d>2016/10/15</d>
<d>2016/09/15</d>
<d>2016/10/18</d>
<d>2016/09/29</d>
<d>2017/09/17</d>
</t>
the wanted, correct result is produced:
<d>2016/10/18</d>
<d>2017/09/17</d>
II. If the date that has the same month's highest day is wanted -- regardless of the year, this transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:for-each-group select="d" group-by="substring(.,6,2)">
<xsl:sequence select=
"current-group()[substring(.,9,2) eq max(current-group()/substring(.,9,2))][1]"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document (above), the correct result is produced:
<d>2016/10/18</d>
<d>2016/09/29</d>
III. If the dates are given together as a string:
Just use the tokenize() standard XPath 2.0 fy=unction.
For example, the equivalent of the first transformation above becomes:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vDates"
select="'2016/10/15 2016/09/15 2016/10/18 2016/09/29 2017/09/17'"/>
<xsl:template match="/">
<xsl:for-each-group select="tokenize($vDates, '\s+')[.]" group-by="substring(.,6,2)">
<xsl:sequence select="max(current-group())"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Dynamic document lookup

I’m trying to come up with a way to dynamically determine which XML document to use for lookups. I parse the input XML document and based on the value I would like to set the appropriate lookup document to use. Ideally I would to have $ LookupDoc set to the correct document to read. The code snippet I have below doesn’t work. I could switch to XSLT 3.0 if that makes it easier.
<?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" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="table-lookup" match="Row" use="#Key1"/>
<xsl:variable name="LookupLTE" select="document('HuaweiLTE.xml')/Huawei"/>
<xsl:variable name="LookupHSPA" select="document('HuaweiHSPA.xml')/Huawei"/>
<xsl:template match="measCollecFile/measData">
<xsl:variable name="DeviceName" select="#userLabel"/>
<xsl:choose>
<xsl:when test="substring($DeviceName,1,1)='L'">
<xsl:variable name="LookupDoc" select="$LookupLTE"/>
</xsl:when>
<xsl:when test="substring($DeviceName,1,1)='H'">
<xsl:variable name="LookupDoc" select="$LookupHSPA"/>
</xsl:when>
</xsl:choose>
<root>
<xsl:for-each select="measInfo">
<xsl:call-template name="loop"> </xsl:call-template>
</xsl:for-each>
</root>
</xsl:template>
You have not shown any use of the variable named LookupDoc you seem to want to define but I think you can simply use <xsl:variable name="LookupDoc" select="if (substring($DeviceName,1,1)='L') then $LookupLTE else if (substring($DeviceName,1,1)='H') then $LookupHSPA else ()"/> to define the variable.

parsing with XSLT

I am struggling a bit with a solution for the following problem and was hoping someone could point me in the right direction.
To illustrate the issue I will try and pair it down to its most simple form. I have the following data:
<?xml version="1.0" encoding="UTF-8"?>
<SampleData>
<Data>AA-BRAND1,BB-BRAND1,AA-BRAND2</Data>
</SampleData>
and need to produce the following output:
<?xml version="1.0" encoding="UTF-8"?>
<ListOfBrandSales>
<BrandSales>
<BrandChannel>AA</BrandChannel>
<ListOfBrand>
<Brand>BRAND1</Brand>
<Brand>BRAND2</Brand>
</ListOfBrand>
</BrandSales>
<BrandSales>
<BrandChannel>BB</BrandChannel>
<Brand>BRAND1</Brand>
</BrandSales>
</ListOfBrandSales>
I have been playing with the tokenize and distinct-values functions but am unable to get it. Seems like I need to nest these functions and not sure if it is possible. My apologies if the solution is obvious but I am a bit new to XSLT.
Thanks in advance.
How about:
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:variable name="tokens" select="tokenize(/SampleData/Data, ',')" />
<xsl:variable name="channels">
<xsl:for-each select="$tokens">
<channel><xsl:value-of select="substring-before(., '-')"/></channel>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<ListOfBrandSales>
<xsl:for-each select="distinct-values($channels/channel)">
<BrandSales>
<BrandChannel><xsl:value-of select="."/></BrandChannel>
<ListOfBrand>
<xsl:for-each select="$tokens[starts-with(., current())]">
<Brand><xsl:value-of select="substring-after(., '-')"/></Brand>
</xsl:for-each>
</ListOfBrand>
</BrandSales>
</xsl:for-each>
</ListOfBrandSales>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
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:template match="/">
<ListOfBrandSales>
<xsl:for-each-group select="tokenize(SampleData/Data, ',')" group-by="substring-before(., '-')">
<BrandSales>
<BrandChannel><xsl:value-of select="current-grouping-key()"/></BrandChannel>
<ListOfBrand>
<xsl:for-each select="current-group()">
<Brand><xsl:value-of select="substring-after(., '-')"/></Brand>
</xsl:for-each>
</ListOfBrand>
</BrandSales>
</xsl:for-each-group>
</ListOfBrandSales>
</xsl:template>
</xsl:stylesheet>

XSLT: output " without it being parsed

I am trying to achieve the following XML output:
<Foo bar=""" />
My XSLT file is as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:variable name="quote">
<xsl:text>"</xsl:text>
</xsl:variable>
<Foo bar="{$quote}"/>
</xsl:template>
</xsl:stylesheet>
Unfortunately, this gives me the output:
<Foo bar="""/>
How do I alter my XSLT to output & quot; without it being parsed into either a " character or a & #34;?
Ian Roberts has already made the very good point that it doesn't actually matter. But if you really, really wanted to do this, then in XSLT 2.0 (but not XSLT 1.0) you could make use of a character map, like so:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" use-character-maps="quotes" />
<xsl:character-map name="quotes">
<xsl:output-character character=""" string="&quot;" />
</xsl:character-map>
<xsl:template match="/">
<xsl:variable name="quote">
<xsl:text>"</xsl:text>
</xsl:variable>
<Foo bar="{$quote}"/>
</xsl:template>
</xsl:stylesheet>

Resources