parsing with XSLT - parsing

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>

Related

Pass in reg ex as variable

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

How can we remove blank line without strip-space and indent XSLT2.0

I am trying remove blank lines without stripping spaces and indentation, because my requirement is indented XML. Can anyone help me, please?
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<Export>
<bio id="b0001">
<p>Maciej</p>
</bio>
<funding-group>
<award-group>
<funding-source>
<named-content content-type="funder-name">Narodowe</named-content>
<named-content content-type="funderidentifier">501100004281</named-content>
</funding-source>
<award-id>/19/B/HS4/</award-id>
<award-id>3</award-id>
</award-group>
</funding-group>
</Export>
Expected Result
<?xml version="1.0" encoding="UTF-8"?>
<Export>
<bio id="b0001">
<p>Maciej</p>
</bio>
<funding-group>
<award-group>
<funding-source>
<named-content content-type="funder-name">Narodowe Centrum Nauki</named-content>
<named-content content-type="funderidentifier">10.13039/501100004281</named-content>
</funding-source>
<award-id>/19/B/HS4/</award-id>
<award-id>3</award-id>
</award-group>
</funding-group>
</Export>
XSLT I have tried this xsl but it's not worked properly if i have run this xsl then generate XML single line.
<?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="2.0">
<xsl:output method="xml" omit-xml-declaration="no"/>
<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>

XSLT on XML Get parent Value from a mixed Node

I have an XML with some mixed Nodes,and I want to get just the value of the parent and not the child.
My XML
<?xml version="1.0" encoding="UTF-8"?>
<Records>
<DET>
<detnumber>100126</detnumber>
<EmployeeNo>100126</EmployeeNo>
<action>CHANGE</action>
<first_name> NewHire-4th
<previous>NewHire</previous>
</first_name>
<last_name>Test-Changed 4th
<previous>Test-Changed 3rd</previous>
</last_name>
<birth_name>
NewHire-Changed 4th
<previous>NewHire-Changed 3rd</previous>
</birth_name>
<formal_name>
NewHire-4th Test-Changed 4th
<previous>NewHire Test-Changed 3rd</previous>
</formal_name>
<salutation>
MISS
<previous>MRS</previous>
</salutation>
<email_address>
testHire4#gmail.com
<previous>testHire2#gmail.com</previous>
</email_address>
</DET>
</Records>
Using XSLT 2.0 ,
I am mostly using copy of in my xslt, But the whole Node and its child are being copied. I need to be able to restrict only to the parent.
<xsl:copy-of select="first_name"/>
<xsl:copy-of select="last_name"/>
<xsl:copy-of select="birth_name"/>
<xsl:copy-of select="formal_name"/>
<xsl:copy-of select="salutation"/>
Below is my preferred output
<?xml version="1.0" encoding="UTF-8"?>
<Records>
<DET>
<detnumber>100126</detnumber>
<EmployeeNo>100126</EmployeeNo>
<action>CHANGE</action>
<first_name> NewHire-4th</first_name>
<last_name>Test-Changed 4th</last_name>
<birth_name>NewHire-Changed 4th</birth_name>
<formal_name>NewHire-4th Test-Changed 4th</formal_name>
<salutation>MISS</salutation>
<email_address>testHire4#gmail.com</email_address>
</DET>
</Records>
Check this Code:-
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="previous"/>
</xsl:stylesheet>

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>

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