Splitting up elements into groups of n child elements (follow-up from 69940580) - xslt-1.0

Follow-up from insert page-break after all elements except the last, when position() is not reliable.
How would I achieve the following result (more info below):
<pages>
<page title="Manchuria">
<edit version="2"/>
<edit version="3"/>
</page>
<page-break/>
<page title="Manchuria">
<edit version="4"/>
<edit version="5"/>
</page>
<page-break/>
<page title="Manchuria">
<edit version="6"/>
<edit version="7"/>
</page>
<page-break/>
<page title="Perfect the first time"/>
<page-break/>
<page title="Zombie Librarian">
<edit version="2"/>
</page>
</pages>
From the following input:
<articles>
<article id="er113" title="Manchuria" publishable="true" version="7">
<original>
<article id="xc141" version="1"/>
</original>
<edits>
<article id="er111" version="2"/>
<article id="yu475" version="4"/>
<article id="iu931" version="3"/>
<article id="er111" version="5"/>
<article id="er112" version="6"/>
<article id="er113" version="7"/>
</edits>
</article>
<article id="ww555" title="Perfect the first time" publishable="true" version="1">
<original>
<article id="ww555" version="1"/>
</original>
<edits/>
</article>
<article id="nb741" title="Zombie Librarian" publishable="true" version="2">
<original>
<article id="nc441" version="1"/>
</original>
<edits>
<article id="nb741" version="2"/>
</edits>
</article>
</articles>
starting from the following xslt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<pages>
<xsl:variable name="pages">
<xsl:apply-templates select="/articles/article[#publishable='true']" mode="subfilter"/>
</xsl:variable>
<xsl:apply-templates select="msxsl:node-set($pages)/article" mode="layout"/>
</pages>
</xsl:template>
<xsl:template match="article" mode="subfilter">
<xsl:variable name="id_of_latest_edit">
<xsl:for-each select="edits/article">
<xsl:sort data-type="number" select="#version"/>
<xsl:if test="position()=last()">
<xsl:value-of select="#id"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="#id = $id_of_latest_edit or not(edits/article)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
<xsl:template match="article" mode="layout">
<page title="{#title}">
<xsl:apply-templates select="edits/article" mode="editdetails"/>
</page>
<xsl:if test="position()!=last()">
<page-break/>
</xsl:if>
</xsl:template>
<xsl:template match="article" mode="editdetails">
<xsl:param name="editPos"/>
<edit version="{#version}"/>
</xsl:template>
</xsl:stylesheet>
The problem
A page can have maximum n edit elements (n=2 in this example), and the edits need to be sorted by version.
What I tried
Before the linked question was solved, I tried something along the lines of:
<xsl:template match="article" mode="subfilter">
<xsl:variable name="currentnode" select="."/>
<!-- always print -->
<xsl:apply-templates select="." mode="layout"/>
<!-- print extra pages when number of articles exceeds limit -->
<xsl:if test="count(edits/article) > 2">
<xsl:for-each select="edits/article[(position() mod 2) = 0]">
<xsl:apply-templates select="$currentnode" mode="layout"/>
</xsl:for-each>
</xsl:if>
</xsl:template>
which gets me the multiple pages and I would still need to filter and sort the edit elements. However, the new code (to get the page-breaks) uses the node-set() function and I can't seem to figure out how to merge this approach (if it is even a good idea to try). Is maybe muenchian grouping needed?

I suppose this is one way you could look at it:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/articles">
<pages>
<xsl:variable name="pages">
<xsl:apply-templates select="article[#publishable='true']" mode="preprocess"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($pages)/page"/>
</pages>
</xsl:template>
<xsl:template match="article[not(edits/article)]" mode="preprocess">
<page title="{#title}"/>
</xsl:template>
<xsl:template match="article" mode="preprocess">
<xsl:variable name="title" select="#title" />
<xsl:for-each select="edits/article">
<xsl:sort select="#version" data-type="number" order="ascending"/>
<xsl:if test="position() mod 2 = 1">
<page title="{$title}">
<edit version="{#version}"/>
<xsl:if test="position()!=last()">
<edit version="{#version + 1}"/>
</xsl:if>
</page>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="page">
<xsl:copy-of select="."/>
<xsl:if test="position()!=last()">
<page-break/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note that this assumes versions are numbered consecutively.

Related

insert page-break after all elements except the last, when position() is not reliable

How would I avoid the last page-break element in the result:
<pages>
<page title="Manchuria"/>
<page-break/>
<page title="Zombie Librarian"/>
<page-break/>
<page title="Perfect the first time"/>
<page-break/>
</pages>
Given the following source xml:
<articles>
<article id="az401" title="The Long Goodbye" publishable="true" version="3">
<original>
<article id="aw301" version="1"/>
</original>
<edits>
<article id="az401" version="3"/>
<article id="pe814" version="2"/>
<article id="we453" version="4"/>
</edits>
</article>
<article id="yu475" title="Manchuria" publishable="true" version="4">
<original>
<article id="xc141" version="1"/>
</original>
<edits>
<article id="er111" version="2"/>
<article id="yu475" version="4"/>
<article id="iu931" version="3"/>
</edits>
</article>
<article id="nb741" title="Zombie Librarian" publishable="true" version="2">
<original>
<article id="nc441" version="1"/>
</original>
<edits>
<article id="nb741" version="2"/>
</edits>
</article>
<article id="ww555" title="Perfect the first time" publishable="true" version="1">
<original>
<article id="ww555" version="1"/>
</original>
<edits/>
</article>
<article id="kl922" title="Here we go again" publishable="true" version="1">
<original>
<article id="kl922" version="1"/>
</original>
<edits>
<article id="uy541" version="2"/>
</edits>
</article>
</articles>
And the following xslt code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:key name="article-by-id" match="article" use="#id"/>
<xsl:template match="/">
<pages>
<xsl:apply-templates select="/articles/article[#publishable='true']" mode="subfilter"/>
</pages>
</xsl:template>
<xsl:template match="article" mode="subfilter">
<xsl:variable name="id_of_latest_edit">
<xsl:for-each select="edits/article">
<xsl:sort data-type="number" select="#version"/>
<xsl:if test="position()=last()"><xsl:value-of select="#id"/></xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="is_latest_edit" select="#id = key('article-by-id',$id_of_latest_edit)/#id"/>
<xsl:variable name="has_no_edits" select="not(edits/article)"/>
<xsl:if test="$is_latest_edit or $has_no_edits">
<xsl:apply-templates select="self::article" mode="layout">
<xsl:with-param name="is_last_page" select="position()=last()"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template match="article" mode="layout">
<xsl:param name="is_last_page"/>
<page title="{#title}"/>
<xsl:if test="not($is_last_page)">
<page-break/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
notes
The problem is the use of an extra template for further filtering the original node-set whereby the position() is no longer reliable.
using <xsl:with-param name="is_first_page" select="position()=1"/> would also not work with the example xml.
If it's not possible to filter the articles directly by using a predicate, then you can use a variable to hold the filtered set. Then using the position() function on the result will work correctly:
XSLT 1.0 (+ EXSLT node-set() function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<pages>
<xsl:variable name="pages">
<xsl:apply-templates select="/articles/article[#publishable='true']" mode="subfilter"/>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($pages)/article"/>
</pages>
</xsl:template>
<xsl:template match="article" mode="subfilter">
<xsl:variable name="id_of_latest_edit">
<xsl:for-each select="edits/article">
<xsl:sort data-type="number" select="#version"/>
<xsl:if test="position()=last()">
<xsl:value-of select="#id"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="#id = $id_of_latest_edit or not(edits/article)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
<xsl:template match="article">
<page title="{#title}"/>
<xsl:if test="position()!=last()">
<page-break/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Changing the output of xml using xslt

How to change the output of the xml.
This is the output that i am getting.
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header> </Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</RunObject>
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
And the output that i want is this
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header> </Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</Item>
<Item>
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
I have tried various things but still i am not to make one final change.
I am not able to change <Runobject class="Entity"> to <Item>.
This the xslt code that i used to alter the xml format
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
>
<xsl:output method="xml" indent="yes"/>
**Removes the run tag along with the namespace
<xsl:template match="s:Run">
<xsl:apply-templates select="*"/>
</xsl:template>
**Copies all the tags
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
**Used this code to replace the <RunObject class="entity"> with <Item> but is not working
<xsl:template match="RunObject[#class='Entity']">
<xsl:element name="Item">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
**Removes all namespaces
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
**Copies attributes
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- template to copy the rest of the nodes -->
<xsl:template match="comment() | text() | processing-instruction()">
<xsl:copy/>
</xsl:template>
</xsl:stylesheet>
I am fairly new to xslt so i mght have certain mistakes while writing the code
There are two reasons why your template:
<xsl:template match="RunObject[#class='Entity']">
<xsl:element name="Item">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
does not match anything:
RunObject is in a namespace, and you are not using a prefix;
XML is case-sensitive; the class attribute contains "entity", not "Entity".
It should work if you do:
<xsl:template match="s:RunObject[#class='entity']">
<xsl:element name="Item">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
Other than that, I believe you could shorten the entire thing considerably - say:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates />
</Item>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 Move of one Segment

please tell me, how can i move the Segment "BGM" from "CNT" to "GRP"?
Actually XML:
<?xml version="1.0"?>
<SEEDELFOR>
<AG/>
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME> </INTERCHANGE_CONTROL_NUMBER>
</SENDER>
</SENDER_QUALIFIER>
</RECEIVER>
</RECEIVER_QUALIFIER>
</SYNTAX_IDENTIFIER>
</SYNTAX_VERSION>
<BGM>227</BGM>
<GRP>
</IDENTIFIER_BY>
</IDENTIFIER_SU>
</DATE_4>
</REF_ON>
</GRP>
</CNT>
</SEEDELFOR>
Correct output of my XML Should be:
<?xml version="1.0"?>
<SEEDELFOR>
<AG/>
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME>
<SENDER></SENDER>
</SENDER_QUALIFIER>
</RECEIVER>
</RECEIVER_QUALIFIER>
</SYNTAX_IDENTIFIER>
</SYNTAX_VERSION>
<GRP>
<BGM>227</BGM>
</GRP>
</CNT>
</SEEDELFOR>
Here´s my XSLT: I tried to copy, but i think it´s better to move the segment?
Edit: Thats my full XSLT: Maybe i have to cominate with other copy segments?
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--Copy all of CNT != BGM-->
<xsl:template match="CNT">
<xsl:copy>
<xsl:apply-templates select="*[local-name()!='BGM']" />
</xsl:copy>
</xsl:template>
<!--copy BGM from CNT to GRP-->
<xsl:template match="GRP">
<xsl:element name="GRP">
<xsl:apply-templates />
<xsl:copy-of select="../BGM" />
</xsl:element>
</xsl:template>
<!--copy the data from ADD/CONTACT with Qualifier EM to GRP, so it can be used in the mapping to set EMAIL from Customer-->
<xsl:template match="GRP">
<xsl:copy>
<xsl:for-each select ="./ADD/CONTACT">
<xsl:if test="./QUALIFIER='EM'">
<CONTACT_EMAIL>
<xsl:value-of select="NUMBER"/>
</CONTACT_EMAIL>
</xsl:if>
<xsl:if test="./QUALIFIER='TE'">
<CONTACT_TEL>
<xsl:value-of select="NUMBER"/>
</CONTACT_TEL>
</xsl:if>
<xsl:if test="./QUALIFIER='FX'">
<CONTACT_FAX>
<xsl:value-of select="NUMBER"/>
</CONTACT_FAX>
</xsl:if>
</xsl:for-each>
<!--copy all other nodes-->
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--delete IC node-->
<xsl:template match="IC" />
<!--delete FILTER_SET node-->
<xsl:template match="FILTER_SET" />
<!--delete SEE_AG node-->
<xsl:template match="SEE_AG" />
<!--delete ME node-->
<xsl:template match="ME" />
<!--delete ADD node-->
<xsl:template match="ADD" />
<!-- delete segment (ADD) with specified QUALIFER (SU) -->
<xsl:template match="ADD[QUALIFIER='SU']"/>
<!--delete TRANSPORT_DETAILS node-->
<xsl:template match="TRANSPORT_DETAILS" />
<!--delete PACKAGE_DETAILS node-->
<xsl:template match="PACKAGE_DETAILS" />
<!--delete AMOUNT_DETAILS node-->
<xsl:template match="AMOUNT_DETAILS" />
<!--delete IC node-->
<xsl:template match="CONTACT" />
<!-- delete empty nodes -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(#*|*|comment()|processing-instruction()) and normalize-space()='']"/>
<!-- delete empty nodes -->
</xsl:stylesheet>
Thanks for your help.
Best regards
Julian
The idiomatic approach to "moving" a node from one place to another is:
copy everything as is,
except the node in question, and
copy the node to its new place:
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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- remove BGM -->
<xsl:template match="BGM"/>
<xsl:template match="GRP">
<xsl:copy>
<xsl:apply-templates/>
<!-- add BGM -->
<xsl:copy-of select="../BGM" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you need to modify other nodes, add templates matching them as necessary.
Seems your XML is not well formed. If I have got it right then you can find below xslt to achieve your requirement:
XML can be:
<?xml version="1.0"?>
<SEEDELFOR>
<AG />
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME>
<INTERCHANGE_CONTROL_NUMBER />
<SENDER />
<SENDER_QUALIFIER />
<RECEIVER />
<RECEIVER_QUALIFIER />
<SYNTAX_IDENTIFIER />
<SYNTAX_VERSION />
<BGM>227</BGM>
<GRP>
<IDENTIFIER_BY />
<IDENTIFIER_SU />
<DATE_4 />
<REF_ON />
</GRP>
</CNT>
</SEEDELFOR>
An the solution would be:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="CNT">
<xsl:copy>
<xsl:apply-templates select="*[local-name()!='BGM']" />
</xsl:copy>
</xsl:template>
<xsl:template match="GRP">
<xsl:element name="GRP">
<xsl:apply-templates />
<xsl:copy-of select="../BGM" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.net/eieE3Q5
<!-- remove BGM at CNT-->
<xsl:template match="BGM"/>
<!--copy the data from ADD/CONTACT with Qualifier EM to GRP, so it can be used in the mapping to set EMAIL from Customer-->
<xsl:template match="GRP">
<xsl:copy>
<xsl:for-each select ="./ADD/CONTACT">
<xsl:if test="./QUALIFIER='EM'">
<CONTACT_EMAIL>
<xsl:value-of select="NUMBER"/>
</CONTACT_EMAIL>
</xsl:if>
<xsl:if test="./QUALIFIER='TE'">
<CONTACT_TEL>
<xsl:value-of select="NUMBER"/>
</CONTACT_TEL>
</xsl:if>
<xsl:if test="./QUALIFIER='FX'">
<CONTACT_FAX>
<xsl:value-of select="NUMBER"/>
</CONTACT_FAX>
</xsl:if>
</xsl:for-each>
<!--copy BGM from CNT to GRP -->
<xsl:copy-of select="../BGM" />
<!--copy all other nodes-->
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</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="*[*]">

Custom XSLT Tag

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.