Transformation of text file into an xml using datapower - xslt

I want to transfer a non-xml text file delimited by '|' characters into an xml using Datapower.
Following is file (sample1)
10|20003|24/23/25|23890
Now i have to break this into the following XML
<ResponseType>
<ResCode>10</ResCode>
<Id>20003</Id>
<SoftCode>24/23/25</SoftCode>
<StatusCode>23890</StatusCode>
</ResponseType>
What I did was following--
1>Create a Transform action in the service that will be receiving non-XML requests.
2>Select "Use XSLT specified in this action on a non-XML message" to specify that this is a Binary Transform.
3>Upload the following stylesheet as the Processing Control File.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
version="1.0">
<dp:input-mapping href="sample1.ffd" type="ffd"/>
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:copy-of select="ResponseType"/>
<xsl:call-template name="str:tokenize">
<xsl:with-param name="string" select="string" />
</xsl:call-template>
</xsl:template>
<xsl:template name="str:tokenize">
<xsl:with-param name="string" select="">
str:tokenize('string', '|')
</xsl:with param>
</xsl:template>
</xsl:stylesheet>
and here is my sample1.ffd(which I have uploaded in my local:// directory in Datapower
<File name="ResponseType">
<!-- capture all data into this tag -->
<Field name="ResCode/Id/SoftCode/StatusCode" />
</File>
But I am not getting desired output , I think my xslt is quite wrong
What can I do do to get desired output?

In DataPower using FFD the following should work:
1) Add the FFD file (below one of my old education samples):
<File name="CSVFILE">
<Group name="CSVLine" minOccurs="0" maxOccurs="unbounded" delim="\n">
<Field name="id"/>
<Field name="fname" delim=","/>
<Field name="lname" delim=","/>
<Field name="title" delim=","/>
<Field name="dept" delim=","/>
<Field name="org"/>
</Group>
</File>
2) Add the XSLT (this one simply copies the FFD transformed XML to output):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
version="1.0">
<dp:input-mapping href="CSVFILE.FFD" type="ffd"/>
<!-- This stylesheet copies the input to the output -->
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
3) Send a message:
1,Anders,Wasen,B2B Architect,DataPower Dev,Enfo Zystems
2,Jean-Luc,Piccard,Captain,USS Enterprise,Star Fleet
4) This will result in the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<CSVFILE>
<CSVLine>
<id>1</id>
<fname>Anders</fname>
<lname>Wasen</lname>
<title>B2B Architect</title>
<dept>DataPower Dev,Enfo Zystems</dept>
<org/>
</CSVLine>
<CSVLine>
<id>2</id>
<fname>Jean-Luc</fname>
<lname>Piccard</lname>
<title>Captain</title>
<dept>USS Enterprise,Star Fleet</dept>
<org/>
</CSVLine>
</CSVFILE>
Make sure that you change the XSLT Transform action into "Transform Binary" and set Request Type to "non-xml", else it will not work!
Hope this will help you! :)

I'm not sure how IBM Datapower might solve this problem, but for XSLT, you would at least wrap your input in a XML element:
<Whatever>
10|20003|24/23/25|23890
</Whatever>
And then you could go on with a transformation like follows. The hard part is splitting your text input. In XSLT 1.0, there is no function available for that, so you need a recursive template.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" version="1.0" exclude-result-prefixes="msxml">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:variable name="tokenized">
<items>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="//text()" />
</xsl:call-template>
</items>
</xsl:variable>
<ResponseType>
<ResCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[1]/text()" />
</ResCode>
<Id>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[2]/text()" />
</Id>
<SoftCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[3]/text()" />
</SoftCode>
<StatusCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[4]/text()" />
</StatusCode>
</ResponseType>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="string" />
<xsl:variable name="item" select="normalize-space( substring-before( concat( $string, '|'), '|'))" />
<xsl:if test="$item">
<item>
<xsl:value-of select="$item" />
</item>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="substring-after($string,'|')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Related

Selecting distinct values from XSLT sub query

I have this XML document:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<properties>
<property name="prop1" type="type1"/>
<property name="prop2" type="type2"/>
<property name="prop3" type="type3"/>
<property name="prop4" type="type1"/>
</properties>
<types>
<type name="type1" group="group1"/>
<type name="type2" group="group1"/>
<type name="type3" group="group2"/>
<type name="type4" group="group3"/>
</types>
<groups>
<group name="group1" owner="owner1"/>
<group name="group2" owner="owner2"/>
<group name="group3" owner="owner3"/>
</groups>
</metadata>
I am transforming it using this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="docRoot" select="/" />
<xsl:for-each select="distinct-values($docRoot/metadata/properties/property/#type)">
<xsl:variable name="groupOwner" select="$docRoot/metadata/groups/group[#name=$docRoot/metadata/types/type[#name=current()]/#group]/#owner" />
<xsl:value-of select="$groupOwner"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
What I want to do is print out the list of unique group owners for all of the properties in the document. I'm successfully filtering out duplicate types with distinct-values but can't see how to filter out duplicate owners.
The current output:
owner1
owner1
owner2
The required output:
owner1
owner2
If it helps no two groups have the same owner.
Keys can be your friend here...
<xsl:key name="types" match="type" use="#name" />
<xsl:key name="groups" match="group" use="#name" />
Then you can do this, without even any need for distinct-values because you won't get duplicate nodes returned this way:
<xsl:for-each select="key('groups', key('types', metadata/properties/property/#type)/#group)">
For example, try this XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="types" match="type" use="#name" />
<xsl:key name="groups" match="group" use="#name" />
<xsl:template match="/">
<xsl:for-each select="key('groups', key('types', metadata/properties/property/#type)/#group)">
<xsl:value-of select="concat(#owner, '
')" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
In fact, you can simplify the xsl:for-each to this:
<xsl:value-of select="key('groups', key('types', metadata/properties/property/#type)/#group)/#owner" separator="
" />
Ah, just needed to try a bit harder:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="docRoot" select="/" />
<xsl:for-each select="distinct-values($docRoot/metadata/groups/group[#name=$docRoot/metadata/types/type[#name=$docRoot/metadata/properties/property/#type]/#group]/#owner)">
<xsl:value-of select="current()"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Open to any suggestions for simplifying this though!

How to merge two documents without overwriting elements in original using XSLT 1.0?

I am a new to XSLT and I need to insert an xslt script into a third party software, which uses XSLT 1.0 to transform xml document.
My task is to take document A.xml and insert each element from document B.xml, but only if the text in A is not yet existing. The output should be generated as document C.xml.
Example A.xml:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Name>SCHAME.table_name</Name>
<Location>oracle:TNS_1</Location>
<Citation>
<Title>Title 1</Title>
<Description/>
</Citation>
<metadataDate>20170418</metadataDate>
</Table>
</metadata>
Example B.xml:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Citation>
<Title>Template Title</Title>
<Abstract>Template Abstract</Abstract>
<Description>Template Description</Description>
</Citation>
<MetadataDate>20160131</MetadataDate>
</Table>
</metadata>
The expected output of C.xml is:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Name>SCHAME.table_name</Name>
<Location>oracle:TNS_1</Location>
<Citation>
<Title>Title 1</Title>
<Abstract>Template Abstract</Abstract>
<Description>Template Description</Description>
</Citation>
<metadataDate>20170418</metadataDate>
</Table>
</metadata>
Three things are important:
B can contain elements, which are not present in A but must be copied to C (eg metadata/Table/Citation/Abstract)
Elements of A with text must not be overwritten in C with text from B. (eg metadata/Table/Citation/Title). Then again empty elements in A must be filled with text from B (eg metadata/Table/Citation/Description)
The xml is just a sample, there are more than hundred different tags in my real xml files, these are just samples depicting my problem. So any solution to my problem has to be applicable on more tags than the ones living in my sample xml.
I do not need a running solution, any hints how to solve this for a XSLT beginner would be nice.
The problem here is how to identify corresponding elements in both files. I assume that each element appears at most once, so we can identify corresponding elements simply by their element name.
My solution follows the idea of John Bollinger:
Create a template.xml file containing all possible elements in the desired output structure.
The XSL script runs through all elements of the template.
It lookups the values of this element in files A and B.
If value A is not empty, take it, else take the value from B.
This is the template I used:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Name/>
<Location/>
<Citation>
<Title />
<Abstract />
<Description/>
</Citation>
<metadataDate/>
</Table>
</metadata>
The following XSL has the above template as input:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Parameters -->
<xsl:param name="aFile" select="'a.xml'" />
<xsl:param name="bFile" select="'b.xml'" />
<!-- Variables -->
<xsl:variable name="aDoc" select="document( $aFile, . )"/>
<xsl:variable name="bDoc" select="document( $bFile, . )"/>
<!-- Locate elements by name in both files -->
<xsl:key name="elementsByName" match="*" use="name()" />
<!-- Root-Template -->
<xsl:template match="/">
<xsl:comment>
<xsl:value-of select="concat( 'Merge of ', $aFile, ' with ', $bFile )" />
</xsl:comment>
<xsl:apply-templates />
</xsl:template>
<!-- Merge all elements -->
<xsl:template match="*">
<xsl:variable name="elemName" select="name()" />
<xsl:variable name="aValue" select="$aDoc/key('elementsByName', $elemName)/text()" />
<xsl:variable name="bValue" select="$bDoc/key('elementsByName', $elemName)/text()" />
<xsl:copy>
<xsl:choose>
<xsl:when test="$aValue != ''">
<xsl:value-of select="$aValue" />
</xsl:when>
<xsl:when test="$bValue != ''">
<xsl:value-of select="$bValue" />
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The result of the transformation is:
<?xml version="1.0" encoding="UTF-8"?>
<!--Merge of a.xml with b.xml-->
<metadata>
<Table>
<Name>SCHAME.table_name</Name>
<Location>oracle:TNS_1</Location>
<Citation>
<Title>Title 1</Title>
<Abstract>Template Abstract</Abstract>
<Description>Template Description</Description>
</Citation>
<metadataDate>20170418</metadataDate>
</Table>
</metadata>
The only "trick" in this code is the use of xsl:key and the corresponding key() function. This allows us to find the corresponding elements (same name) in both files A and B.
This script copies all elements from the template to the output file C.
To change this behaviour, simple move the xsl:copy instructions inside of the xsl:when.
I try to give an improved answer separately to avoid confusion with the helpful comments there.
In addition to the "generic" approach there (match element names), we will now have specific templates as well.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Parameters -->
<xsl:param name="aFile" select="'a.xml'" />
<xsl:param name="bFile" select="'b.xml'" />
<!-- Variables -->
<xsl:variable name="aDoc" select="document( $aFile, . )"/>
<xsl:variable name="bDoc" select="document( $bFile, . )"/>
<!-- Locate elements by name in both files -->
<xsl:key name="elementsByName" match="*" use="name()" />
<!-- Root-Template -->
<xsl:template match="/">
<xsl:comment>
<xsl:value-of select="concat( 'Merge of ', $aFile, ' with ', $bFile )" />
</xsl:comment>
<xsl:apply-templates />
</xsl:template>
<!-- Merge specific elements -->
<xsl:template match="metadata/Table/Description">
<xsl:call-template name="mergeElement">
<xsl:with-param name="aValue" select="$aDoc/metadata/Table/Description/text()" />
<xsl:with-param name="bValue" select="$bDoc/metadata/Table/Description/text()" />
</xsl:call-template>
</xsl:template>
<xsl:template match="metadata/Table/Citation/Description">
<xsl:call-template name="mergeElement">
<xsl:with-param name="aValue" select="$aDoc/metadata/Table/Citation/Description/text()" />
<xsl:with-param name="bValue" select="$bDoc/metadata/Table/Citation/Description/text()" />
</xsl:call-template>
</xsl:template>
<!-- Merge unique elements -->
<xsl:template match="*" priority="-10">
<xsl:variable name="elemName" select="name()" />
<xsl:call-template name="mergeElement">
<xsl:with-param name="aValue" select="$aDoc/key('elementsByName', $elemName)/text()" />
<xsl:with-param name="bValue" select="$bDoc/key('elementsByName', $elemName)/text()" />
</xsl:call-template>
</xsl:template>
<!-- Use A or B -->
<xsl:template name="mergeElement">
<xsl:param name="aValue" />
<xsl:param name="bValue" />
<xsl:copy>
<xsl:choose>
<xsl:when test="$aValue != ''">
<xsl:value-of select="$aValue" />
</xsl:when>
<xsl:when test="$bValue != ''">
<xsl:value-of select="$bValue" />
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
For the test, I changed the template and A and B and added another Description element directly inside of Table:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Name />
<Description/> <!-- NEW: not unique element name -->
<Location/>
<Citation>
<Title />
<Abstract />
<Description/>
</Citation>
<metadataDate/>
</Table>
</metadata>
File A:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Name>SCHAME.table_name</Name>
<Location>oracle:TNS_1</Location>
<Description>Table description A</Description> <!-- NEW -->
<Citation>
<Title>Title 1</Title>
<Description/>
</Citation>
<metadataDate>20170418</metadataDate>
</Table>
</metadata>
File B:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<Table>
<Description>Table description B</Description> <!-- NEW -->
<Citation>
<Title>Template Title</Title>
<Abstract>Template Abstract</Abstract>
<Description>Template Description</Description>
</Citation>
<MetadataDate>20160131</MetadataDate>
</Table>
</metadata>
Generated file C:
<?xml version="1.0" encoding="UTF-8"?>
<!--Merge of a.xml with b.xml-->
<metadata>
<Table>
<Name>SCHAME.table_name</Name>
<Description>Table description A</Description> <!-- NEW -->
<Location>oracle:TNS_1</Location>
<Citation>
<Title>Title 1</Title>
<Abstract>Template Abstract</Abstract>
<Description>Template Description</Description>
</Citation>
<metadataDate>20170418</metadataDate>
</Table>
</metadata>
Obviosly, this is not a generic solution.
But if the number of non-unique elements is low compared to the total number, you will benefit from the generical template. Only the non-unique elements must be "styled" individually.

Convert string value as XML tag name

Below is my requirement. Can we do this using XSLT? I want to convert value of AttributeName as tag under policy and corresponding AttributeValue as value.
Input :
<Policy>
<Attributes>
<AttributeName>is_policy_loan</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_owners</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_twoyears</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
</Policy>
Output :
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>
The following xsl file will do the job:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- create the <AttributeName>AttributeValue</..> nodes -->
<xsl:template match="//Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
<!-- wrap nodes in a `Policy` node -->
<xsl:template match="/">
<Policy>
<xsl:apply-templates/>
</Policy>
</xsl:template>
</xsl:stylesheet>
The way i would do,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="Policy">
<xsl:element name="Policy">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output will be,
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>

XSLT: Avoiding indentation in some nodes

I have some XSLT that replaces linebreaks with <Break/> tags and it works fine as long as there isn't multiple consecutive linebreaks. I think it's the indent="yes" that's causing problems.
Can it be disabled for some nodes?
Basically nodes with mixed content (text and elements) can not contain any linebreaks.
The input xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Account xmlns="http://example.com/account">
<Owner>
<ID>012345789</ID>
<Name>Peter Johnson</Name>
</Owner>
<Notes>
<NoteID>012345789</NoteID>
<Text>This is the description:
Line 1
Line 2
Line 3
Line 4, after double linebreak
Line 5</Text>
</Notes>
</Account>
The XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://example.com/account" version="1.0">
<xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes"/>
<xsl:template name="replace_sab">
<!-- with string s, replace substring a by string b -->
<!-- s, a and b are parameters determined upon calling -->
<xsl:param name="s" />
<xsl:param name="a" />
<xsl:param name="b" />
<xsl:choose>
<xsl:when test="contains($s,$a)">
<xsl:value-of select="substring-before($s,$a)" />
<xsl:copy-of select="$b" />
<xsl:call-template name="replace_sab">
<xsl:with-param name="s" select="substring-after($s,$a)" />
<xsl:with-param name="a" select="$a" />
<xsl:with-param name="b" select="$b" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()[not(normalize-space())]"/>
<xsl:template match="text()[boolean(normalize-space())]">
<xsl:call-template name="replace_sab">
<xsl:with-param name="s" select="." />
<xsl:with-param name="a" select="'
'" />
<xsl:with-param name="b"><Break/></xsl:with-param>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
The output that I get:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Account xmlns="http://example.com/account">
<Owner>
<ID>012345789</ID>
<Name>Peter Johnson</Name>
</Owner>
<Notes>
<NoteID>012345789</NoteID>
<Text>This is the description:<Break/>Line 1<Break/>Line 2<Break/>Line 3<Break/>
<Break/>Line 4, after double linebreak<Break/>Line 5</Text>
</Notes>
</Account>
The output I would like:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Account xmlns="http://example.com/account">
<Owner>
<ID>012345789</ID>
<Name>Peter Johnson</Name>
</Owner>
<Notes>
<NoteID>012345789</NoteID>
<Text>This is the description:<Break/>Line 1<Break/>Line 2<Break/>Line 3<Break/><Break/>Line 4, after double linebreak<Break/>Line 5</Text>
</Notes>
</Account>
I am using "TIBCO XSLT 1.0" XSLT engine in a Tibco BusinessWorks process.
There's no standard way of doing this.
If you were using Saxon you could use the saxon:suppress-indentation output parameter, which becomes a standard option in XSLT 3.0.
Perhaps you could find a way of inserting the Saxon serializer into your processing pipeline even if you stick with the Tibco XSLT engine.

XSLT CallTemplate ForEach XML

I need a little XSLT help. Couldn't figure out why the actual output is different from my expected output. Any help much appreciated!
XML
<?xml version="1.0"?>
<a>
<b c="d"/>
<b c="d"/>
<b c="d"/>
</a>
XSL
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="foo">
<xsl:param name="content"></xsl:param>
<xsl:value-of select="$content"></xsl:value-of>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="foo">
<xsl:with-param name="content">
<xsl:for-each select="a/b">
<e>
<xsl:value-of select="#c" />
</e>
</xsl:for-each>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Actual Output
<?xml version="1.0"?>
ddd
Desired Output
<?xml version="1.0"?>
<e>d</e>
<e>d</e>
<e>d</e>
Note: Calling the template is mandatory. In my situation the template does more with extension functions.
Contrary to what ABach says, your xsl:param is fine. The only thing you need to change is your xsl:value-of. It should be a xsl:copy-of:
<xsl:template name="foo">
<xsl:param name="content"/>
<xsl:copy-of select="$content"/>
</xsl:template>
You're very close; you've just mixed up relative positioning and correct parameter usage within templates. Here's a slightly revised answer.
When this XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template name="foo">
<xsl:param name="pContent" />
<xsl:for-each select="$pContent">
<e>
<xsl:value-of select="#c" />
</e>
</xsl:for-each>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="foo">
<xsl:with-param name="pContent" select="*" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
...is applied to the original XML:
<?xml version="1.0"?>
<a>
<b c="d" />
<b c="d" />
<b c="d" />
</a>
...the desired result is produced:
<?xml version="1.0"?>
<e>d</e>
<e>d</e>
<e>d</e>
In particular, notice the correct usage of <xsl:param> to include nodes based on their relative position. In your case, you are telling the XSLT parser to output the text values of the parameter that you're passing, rather than altering the nodes' contents in the way you want.