XSLT adding tag in specific pattern - xslt-2.0

I have a issue with adding a "P" tag before next pattern matching .
Source i am getting is:
<root>
<Element>
<P>Value1</P>
<P>
<Level1>
<Level2 type="i">Name1</Level2>, Title1
</Level1>, Text1
<Level1>
<Level2 type="i">Name2</Level2>
</Level1>, Text2.</P>
<P>
<Level1>
<Level2 type="i">Name3</Level2>, Title2
</Level1>, Text3.
</P>
</Element>
Desired out i want is
<root>
<Element>
<P>Value1</P>
<P>
<Level1>
<Level2 type="i">Name1</Level2>, Title1
</Level1>, Text1
</P>
<P>
<Level1>
<Level2 type="i">Name2</Level2>
</Level1>, Text2.</P>
<P>
<Level1>
<Level2 type="i">Name3</Level2>, Title2
</Level1>, Text3.
</P>
</Element>
</root>
The following template does not give me desired result, Please help
<xsl:template match="Element">
<xsl:copy>
<xsl:for-each-group select="P" group-starting-with="Level1/Level2">
<P>
<xsl:sequence select="current-group()"/>
</P>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>

I think you need to use the for-each-group on a template matching P and then you get
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="P">
<xsl:for-each-group select="node()" group-starting-with="Level1[Level2]">
<P>
<xsl:apply-templates select="current-group()"/>
</P>
</xsl:for-each-group>
</xsl:template>
</xsl:transform>
which at http://xsltransform.net/gVhD8QT gives the result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Element>
<P>Value1</P>
<P>
<Level1>
<Level2 type="i">Name1</Level2>, Title1
</Level1>, Text1
</P>
<P>
<Level1>
<Level2 type="i">Name2</Level2>
</Level1>, Text2.</P>
<P>
<Level1>
<Level2 type="i">Name3</Level2>, Title2
</Level1>, Text3.
</P>
</Element>
</root>

Related

How can grouped the TEXT/ELEMENT by xsl:for-each-group element in XSLT 2.0

INPUT XML:
<overline-start id="tie1" specific-use="tie-bar"/>PtCl<sub>2</sub>(P((CH<sub>2</sub>)<sub><italic toggle="yes">n</italic></sub>)<sub>3</sub><overline-end rid="tie1"/>
EXPECTED XML:
<overline id="tie1" specific-use="tie-bar">PtCl<sub>2</sub>(P((CH<sub>2</sub>)<sub><italic toggle="yes">n</italic></sub>)<sub>3</sub></overline>
MY XSLT 2.0 Code:
<xsl:template match="overline-start">
<xsl:for-each-group select="self::overline-start" group-adjacent="self::overline-start[following-sibling::overline-end]">
<xsl:for-each select="current-group()">
<overline>
<xsl:apply-templates select="#*"/>
<xsl:copy-of select="current-group()"/>
</overline>
</xsl:for-each>
As you mentioned the requirement in comments, I tried it #Martin Honnen 's way:
Assuming input as:
<?xml version="1.0" encoding="UTF-8"?>
<p>
<overline-start id="tie1" specific-use="tie-bar"/>PtCl<sub>2</sub>(P((CH<sub>2</sub>)<sub><italic toggle="yes">n</italic></sub>)<sub>3</sub><overline-end rid="tie1"/>
</p>
A 2.0 solution can be:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="no" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<overline>
<xsl:for-each-group select="* | text()" group-starting-with="overline-start">
<xsl:for-each-group select="current-group()" group-ending-with="overline-end">
<xsl:apply-templates select="#*" />
<xsl:sequence select="(current-group() except .) [position() != last()]" />
</xsl:for-each-group>
</xsl:for-each-group>
</overline>
</xsl:template>
</xsl:stylesheet>
http://xsltfiddle.liberty-development.net/bnnZW8/1

XSLT How to copy following elements inside the new group

I have this flat xml. i need to group the contents on h1/title and copy all following para's until next h1/title pattern if exists else add empty para.
Source XML:
<Element>
<h1>
<title>Name1</title>
</h1>
<h1>
<title>Name2</title>
</h1>
<para>Test1</para>
<para>Test2</para>
<h1>
<title>Name3</title>
</h1>
<para>Test3</para>
<para>Test4</para>
</Element>
I want the output like below.
<Element>
<group>
<h1>
<title>Name1</title>
</h1>
<para> </para>
</group>
<group>
<h1>
<title>Name2</title>
</h1>
<para>Test1</para>
<para>Test2</para>
</group>
<group>
<h1>
<title>Name3</title>
</h1>
<para>Test3</para>
<para>Test4</para>
</group>
</Element>
so far I have tried following template, it does not copy following para's.
<xsl:template match="h1">
<group>
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="h1">
<xsl:choose>
<xsl:when test="self::h1">
<group>
<xsl:apply-templates select="current-group()"/>
<xsl:apply-templates select="following-sibling::para[not(following::h1)]"/>
</group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</group>
</xsl:template>
I would suggest to group the children of the Element element and then of course inside of the for-each-group you can simply check whether there is no second group item to add the empty para:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Element">
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="h1">
<xsl:choose>
<xsl:when test="self::h1">
<group>
<xsl:apply-templates select="current-group()"/>
<xsl:if test="not(current-group()[2])">
<para/>
</xsl:if>
</group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
Online at http://xsltransform.net/naZXpX7.

XSL:KEY Function for external file

I have an issue in key function, key function not running in following code.
my input is
XML (Keys.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Keys>
<Key year="2001" name="ABC"/>
<Key year="2002" name="BCA"/>
</Keys>
XML to convert
<?xml version="1.0" encoding="UTF-8"?>
<p>
<text> .. .. <key>ABC</key> ...</text>
<text> .. .. <key>BCA</key> ...</text>
</p>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:key name="keydata" match="*[name() = document('keys.xml')]/Keys/Key" use="#name"/>
<xsl:template match="key">
<xsl:copy>
<xsl:attribute name="ref"><xsl:value-of select="key('keydata', .)/#year"/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output
<p>
<text> .. .. <key ref="">ABC</key> ...</text>
<text> .. .. <key ref="">BCA</key> ...</text>
</p>
Desired Ouput
<p>
<text> .. .. <key ref="2001">ABC</key> ...</text>
<text> .. .. <key ref="2002">BCA</key> ...</text>
</p>
This should work:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:variable name="keys" select="document('keys.xml')" as="document-node()"/>
<xsl:key name="keydata" match="Key" use="#name"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="key">
<xsl:copy>
<xsl:attribute name="ref"><xsl:value-of select="$keys/key('keydata', current())/#year"/></xsl:attribute>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In the XSL specification, there's an example entitled "Example: Using Keys to Reference other Documents" that exactly matches your use case.
This is the resulting document:
<?xml version="1.0" encoding="UTF-8"?>
<p>
<text> .. .. <key ref="2001">ABC</key> ...</text>
<text> .. .. <key ref="2002">BCA</key> ...</text>
</p>
Change <xsl:key name="keydata" match="*[name() = document('keys.xml')]/Keys/Key" use="#name"/> to <xsl:key name="keydata" match="Keys/Key" use="#name"/> and then <xsl:attribute name="ref"><xsl:value-of select="key('keydata', .)/#year"/></xsl:attribute> to <xsl:attribute name="ref" select="key('keydata', . doc('Keys.xml'))/#year"/></xsl:attribute>.

Extracting value from External xml based on some rule in xsl

This is input xml input.xml
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
This is Prop.xml
<?xml version="1.0" encoding="utf-8"?>
<properties>
<code dpsi="0BZG" docid="asdww">HKBL1.0001.lohk.CAP65</code>
<code dpsi="0BZH" docid="navin">HKBL1.0002.aohk.CAP383</code>
<code no="3">345</code>
</properties>
This is desired output
<root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
IF prop.xml code/text matches remotelink/#refptid than copy attribute of prop.xml to remotelink otherwise no changes in remotelink.
This is the XSLT I have written. So far I am not getting result for unmatched condition:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink[#service='DOC-ID']" name="t-remote">
<xsl:variable name="refptid" select="./#refpt"/>
<xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>
<xsl:for-each select="$path/properties/code">
<xsl:choose>
<xsl:when test="./text()=$refptid">
<xsl:element name="remotelink">
<xsl:attribute name="DOC-ID" select="./#docid"/>
<xsl:attribute name="dpsi" select="./#dpsi"/>
<xsl:attribute name="refpt" select="$refptid"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:if test="./#docrefid"/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="remotelink[#service='DOC-ID']"> will never never be triggered, there's no service attribute on the <remotelink> element. Moreover, you don't retrieve the correct attribute (refpt instead of refptid)
This XSL transformation should do the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xlsx="http://www.stylusstudio.com/XSLT/XLSX" xmlns:spml="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:saxon="http://saxon.sf.net/" version="2.0">
<xsl:variable name="props.doc" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')/properties" />
<xsl:template match="#*|node()" name="root">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="remotelink" name="t-remote">
<xsl:variable name="refptid" select="./#refptid"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:if test="$props.doc/code[. = $refptid]">
<xsl:apply-templates select="$props.doc/code[. = $refptid]/#*"/>
</xsl:if>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is the result of the transformation:
<?xml version="1.0" encoding="UTF-8"?><root>
<bodytext>
<remotelink refptid="HKBL1.0001.lohk.CAP65" dpsi="0BZG" docid="asdww">some text</remotelink>
<remotelink refptid="HKBL1.0001.lohk.CAP199999">some text</remotelink>
</bodytext>
</root>
I would simply define a global parameter or variable <xsl:variable name="path" select="doc('file:/C:/Users/DalalNS/Desktop/xslt/prop.xml')"/>, then set up a key <xsl:key name="prop" match="code" use="."/>, and then use that in a template
<xsl:template match="remotelink[key('prop', #refptid, $path)]">
<xsl:copy>
<xsl:copy-of select="key('prop', #refptid, $path)/#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
that, together with your first template, should suffice.

How to turn-off showing child items for selected Main Menu items in Umbraco CMS?

In order to explain my issue easier, please first see this photo
On my template I have already configured Top navigation but problem is that I am trying to make new menu Item which will be called 'News' and thing is that I don't want that every out of 99 news items I will publish (in next two months), to be autmatically available as sumbenu child item under 'News'.
As I noticed, most of the configuration is under 'umbTopNavigation.xslt'
]>
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="msxml umbraco.library">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:param name="currentPage"/>
<!-- Input the documenttype you want here -->
<xsl:variable name="level" select="1"/>
<xsl:template match="/">
<ul id="topNavigation">
<li class="home">
<xsl:if test="$currentPage/#id = $currentPage/ancestor-or-self::* [#level=$level]/#id">
<xsl:attribute name="class">home current</xsl:attribute>
</xsl:if>
Home
</li>
<xsl:for-each select="$currentPage/ancestor-or-self::* [#level=$level]/* [#isDoc and string(umbracoNaviHide) != '1']"> <li>
<xsl:if test="#id = $currentPage/#id">
<xsl:attribute name="class">current</xsl:attribute>
</xsl:if>
<a class="navigation" href="{umbraco.library:NiceUrl(#id)}">
<span><xsl:value-of select="#nodeName"/></span>
</a> </li>
</xsl:for-each> </ul>
</xsl:template>
but I can't figure out what exactly I need to change?
Any help is appreciated and many thanks in advance!
MC2012
First macro
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="msxml umbraco.library">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<xsl:variable name="items" select="$currentPage/ancestor-or-self::* [#isDoc and #level = 2]/* [#isDoc and string(umbracoNaviHide) != '1']"/>
<!-- The fun starts here -->
<xsl:if test="count($items) > 0">
<ul>
<xsl:for-each select="$items">
<li>
<a href="{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName"/>
</a>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Second Macro
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="umbraco.library"
>
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:param name="currentPage" />
<!-- Specify level of the top node for a language (usually 1) -->
<xsl:variable name="level" select="1" />
<!-- Grab the top node -->
<xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[#level = $level]" />
<xsl:template match="/">
<ul class="menu">
<!-- Create the Home link -->
<li>
<xsl:if test="$currentPage/#id = $siteRoot/#id">
<xsl:attribute name="class">sel</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl($siteRoot/#id)}">
<xsl:value-of select="$siteRoot/#nodeName" />
</a>
</li>
<!-- Process all children of $siteRoot (if any) -->
<xsl:apply-templates select="$siteRoot/*[#isDoc][not(umbracoNaviHide = 1)]" />
</ul>
</xsl:template>
<!-- Generic template for all nav links -->
<xsl:template match="*[#isDoc]">
<li>
<xsl:if test="#id = $currentPage/#id">
<xsl:attribute name="class">sel</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName" />
</a>
<!-- Render sub menu if necessary -->
<xsl:call-template name="submenu" />
</li>
</xsl:template>
<!-- Template for Inactive links -->
<xsl:template match="*[#isDoc][umbracoNaviInactive = 1]">
<li>
<xsl:if test="#id = $currentPage/#id">
<xsl:attribute name="class">sel</xsl:attribute>
</xsl:if>
<span>
<xsl:value-of select="#nodeName" />
</span>
<!-- Submenu? -->
<xsl:call-template name="submenu" />
</li>
</xsl:template>
<!-- Template checking for, and processing any submenu -->
<xsl:template name="submenu">
<xsl:variable name="subPages" select="*[#isDoc][not(umbracoNaviHide = 1)]" />
<xsl:if test="$subPages">
<ul>
<xsl:apply-templates select="$subPages" />
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Ok - you have two options. A quick hack or a full solution.
The quick hack is to update the second macro where the subnav is called and exclude the subnav being called where it's the news page (and you can include other pages in here too)
In this example I've used node ID 1107 to be the ID of your news page, but you will need to update this with your actual news page ID.
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="umbraco.library"
>
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:param name="currentPage" />
<!-- Specify level of the top node for a language (usually 1) -->
<xsl:variable name="level" select="1" />
<!-- Grab the top node -->
<xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[#level = $level]" />
<xsl:template match="/">
<ul class="menu">
<!-- Create the Home link -->
<li>
<xsl:if test="$currentPage/#id = $siteRoot/#id">
<xsl:attribute name="class">sel</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl($siteRoot/#id)}">
<xsl:value-of select="$siteRoot/#nodeName" />
</a>
</li>
<!-- Process all children of $siteRoot (if any) -->
<xsl:apply-templates select="$siteRoot/*[#isDoc][not(umbracoNaviHide = 1)]" />
</ul>
</xsl:template>
<!-- Generic template for all nav links -->
<xsl:template match="*[#isDoc]">
<li>
<xsl:if test="#id = $currentPage/#id">
<xsl:attribute name="class">sel</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName" />
</a>
<!-- Render sub menu if necessary excluding the news pages -->
<xsl:if test="not(#id=1107)">
<xsl:call-template name="submenu" />
</xsl:if>
</li>
</xsl:template>
<!-- Template for Inactive links -->
<xsl:template match="*[#isDoc][umbracoNaviInactive = 1]">
<li>
<xsl:if test="#id = $currentPage/#id">
<xsl:attribute name="class">sel</xsl:attribute>
</xsl:if>
<span>
<xsl:value-of select="#nodeName" />
</span>
<!-- Submenu? -->
<xsl:call-template name="submenu" />
</li>
</xsl:template>
<!-- Template checking for, and processing any submenu -->
<xsl:template name="submenu">
<xsl:variable name="subPages" select="*[#isDoc][not(umbracoNaviHide = 1)]" />
<xsl:if test="$subPages">
<ul>
<xsl:apply-templates select="$subPages" />
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The proper way to do it would be to add a property to your document type to include a property to say do not list children or similar, and then test for that in the XSLT, but that's a much more involved fix, and for that I suggest you read up a bit more on how XSLTs work. Let me know if you want to go down that route and there are plenty of resources I can direct you to
thanks,

Resources