XSLT output is not correct - xslt

I am trying to figure out what is wrong with my xsl file. Here is my sample xml file:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="user#host" start="2015-12-30T14:55:40.156Z">
<ls>
<destination value="/test/folder" />
<files>
<file>
<filename value="folder1" />
<type value="d" />
<modification value="2015-03-04T21:09:25.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="folder2" />
<type value="d" />
<modification value="2015-03-04T21:08:47.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="folder3" />
<type value="d" />
<modification value="2015-03-04T21:08:57.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="folder4" />
<type value="d" />
<modification value="2015-03-04T21:09:14.000Z" />
<permissions value="rwxrw-rw-" />
</file>
<file>
<filename value="file1.txt" />
<type value="-" />
<size value="113" />
<modification value="2015-12-30T14:55:07.000Z" />
<permissions value="rw-rw-rw-" />
</file>
</files>
<result success="true" />
</ls>
</session>
This is my xsl file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>filename,filedatetime,filetype</xsl:text>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="file"/>
<xsl:text>-eof-</xsl:text>
</xsl:template>
<xsl:template match="file">
<xsl:text>"</xsl:text>
<xsl:value-of select="filename/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="modification/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="type/#value" />
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
The output that I am getting is:
filename,filedatetime,filetype
-eof-
I cannot seem to figure out how to get the values of "filename", "modification" and "type" elements. Any help would be appreciated.

The line <xsl:apply-templates select="file"/> will only match immediate children of the root node of type file and there aren't any of those.
If you want all file children at any depth you could use <xsl:apply-templates select="//file"/> instead.
The XML has a default namespace, so the elements in your XSL won't match unless their namespaces match too.
The following seems to work (note the definition and use of the xx namespace):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xx="http://winscp.net/schema/session/1.0">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/>
<xsl:template match="/">
<xsl:text>filename,filedatetime,filetype</xsl:text>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="//xx:file" />
<xsl:text>-eof-</xsl:text>
</xsl:template>
<xsl:template match="xx:file">
<xsl:text>"</xsl:text>
<xsl:value-of select="xx:filename/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="xx:modification/#value" />
<xsl:text>","</xsl:text>
<xsl:value-of select="xx:type/#value" />
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Related

XSLT Filtering based on nodes and attributes

I'm new on XSLT and have a requirement to use XSLT to select values from an XML file of this form :
<?xml version="1.0" encoding="utf-8"?>
<deviceInstallation>
<order>
<orderID>296</orderID>
<orderPosID>1</orderPosID>
<action rvcd="2">unInstall</action>
</order>
<deviceInfo>
<actionInfo rvcd="1">Software Install</actionInfo>
<device>
<deviceID>1436</deviceID>
</device>
</deviceInfo>
<deviceInfo>
<actionInfo rvcd="2">Software Uninstall</actionInfo>
<device>
<deviceID>4112</deviceID>
</device>
</deviceInfo>
</deviceInstallation>
I need to filter the elements deviceinfo based on the attribute rvcd = 2 because this is what is defined on the same attribute of child element action of the order element.
I tried to write and xslt and used a var to get the value to filter but don't know how to use it :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" />
<xsl:variable name="separator" select="';'" />
<xsl:variable name="newline" select="'
'" />
<xsl:variable name="actionFilter" select="/deviceInstallation/order/action[]/#rvcd" />
<xsl:template match="/">
<xsl:text>orderID;DeviceID</xsl:text>
<xsl:value-of select="$newline" />
<xsl:for-each select="/deviceInstallation">
<!--OrderID-->
<xsl:value-of select="/deviceInstallation/order/orderID"/>
<xsl:value-of select="$separator"/>
<!--DeviceID-->
<xsl:value-of select="/deviceInstallation/deviceInfo/device/deviceID"/> <!-- here want to filter on rvcd-->
<xsl:value-of select="$separator"/>
<xsl:value-of select="$newline" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Any help appreciated
IIUC, you want to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/deviceInstallation">
<xsl:variable name="orderID" select="order/orderID" />
<xsl:variable name="actionFilter" select="order/action/#rvcd" />
<xsl:text>orderID;DeviceID
</xsl:text>
<xsl:for-each select="deviceInfo[actionInfo/#rvcd=$actionFilter]">
<xsl:value-of select="$orderID" />
<xsl:text>;</xsl:text>
<xsl:value-of select="device/deviceID" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
or perhaps a bit more elegantly:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="dev" match="deviceInfo" use="actionInfo/#rvcd" />
<xsl:template match="/deviceInstallation">
<xsl:variable name="orderID" select="order/orderID" />
<xsl:text>orderID;DeviceID
</xsl:text>
<xsl:for-each select="key('dev', order/action/#rvcd)">
<xsl:value-of select="$orderID" />
<xsl:text>;</xsl:text>
<xsl:value-of select="device/deviceID" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Transformation of text file into an xml using datapower

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>

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 key only returns a value once

I think I'm missing something obvious here but here goes. I have the below xml and I need to group the KEY nodes of the matched instances together. This is specified by the match attribute and it can contain more than one item number. There can be any number of ITEM nodes and any number of KEY nodes. Also, there is no limit to the depth of the ITEM nodes. And, the matched instances need not be under the same parent. I'm also limited to XSLT 1.0 and the Microsoft parser.
<?xml version="1.0" encoding="utf-8" ?>
<ITEM number='1'>
<ITEM number='2'>
<ITEM number='3' match='5,11'>
<KEY name='key1' value='x' />
<KEY name='key2' value='y' />
<KEY name='key3' value='z' />
<ITEM number ='4' />
</ITEM>
<ITEM number='5' match='3,11'>
<KEY name='key1' value='x' />
<KEY name='key2' value='y' />
<KEY name='key3' value='z' />
</ITEM>
<ITEM number='6' match='10'>
<KEY name='key1' value='x' />
<KEY name='key2' value='y' />
<KEY name='key4' value='a' />
</ITEM>
<ITEM number='7' />
<ITEM number='8'>
<KEY name='key1' value='x' />
</ITEM>
</ITEM>
<ITEM number='9'>
<ITEM number='10' match='6'>
<KEY name='key1' value='x' />
<KEY name='key3' value='z' />
<KEY name='key5' value='b' />
</ITEM>
</ITEM>
<ITEM number='11' match='3,5'>
<KEY name='key2' value='y' />
<KEY name='key3' value='z' />
</ITEM>
</ITEM>
My expected result would look something like this...
<?xml version="1.0" encoding="utf-8" ?>
<Result>
<Group number="1" />
<Group number="2" />
<Group number="3,5,11">
<KEY name='key1' value='x' />
<KEY name='key2' value='y' />
<KEY name='key3' value='z' />
</Group>
<Group number="4" />
<Group number="6,10">
<KEY name='key1' value='x' />
<KEY name='key2' value='y' />
<KEY name='key3' value='z' />
<KEY name='key4' value='a' />
<KEY name='key5' value='b' />
</Group>
<Group number="7" />
<Group number="8">
<KEY name='key1' value='x' />
</Group>
<Group number="9" />
</Result>
What I actually get is...
<?xml version="1.0" encoding="utf-8"?>
<Result>
<Group number="1" />
<Group number="2" />
<Group number="3,5,11">
<KEY name="key1" value="x" />
<KEY name="key2" value="y" />
<KEY name="key3" value="z" />
</Group>
<Group number="4" />
<Group number="6,10">
<KEY name="key4" value="a" />
<KEY name="key5" value="b" />
</Group>
<Group number="7" />
<Group number="8" />
<Group number="9" />
</Result>
I'm using a key and it looks like once I access that particular value from the key function, I cannot access it again. Group number 6,10 should contain all 5 keys but is missing the first 3 which are already present in group number 3,5. Similarly for group number 8, it should contain 1 key. I've used recursion to skip over the matched instances but I don't think there is any issue over there, it seems to be related to the key functionality. I've attached my xslt below, please take a look and tell me what I'm doing wrong. Any tips for performance improvements are also appreciated :)
<?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:key name="kKeyByName" match="KEY" use="#name" />
<xsl:template name="ProcessItem">
<!--pItemsList - node set containing items that need to be processed-->
<xsl:param name="pItemsList" />
<!--pProcessedList - string containing processed item numbers in the format |1|2|3|-->
<xsl:param name="pProcessedList" />
<xsl:variable name="vCurrItem" select="$pItemsList[1]" />
<!--Recursion exit condition - check if we have a valid Item-->
<xsl:if test="$vCurrItem">
<xsl:variable name="vNum" select="$vCurrItem/#number" />
<!--Skip processed instances-->
<xsl:if test="not(contains($pProcessedList, concat('|', $vNum, '|')))">
<xsl:element name="Group">
<!--If the item is matched with another item, only the distinct keys of the 2 should be displayed-->
<xsl:choose>
<xsl:when test="$vCurrItem/#match">
<xsl:attribute name="number">
<xsl:value-of select="concat($vNum, ',', $vCurrItem/#match)" />
</xsl:attribute>
<xsl:for-each select="(//ITEM[#number=$vNum or #match=$vNum]/KEY)[generate-id(.)=generate-id(key('kKeyByName', #name)[1])]">
<xsl:apply-templates select="." />
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="number">
<xsl:value-of select="$vNum" />
</xsl:attribute>
<xsl:apply-templates select="KEY" />
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:if>
<!--Append processed instances to list to pass on in recursive function-->
<xsl:variable name="vNewList">
<xsl:value-of select="$pProcessedList" />
<xsl:value-of select="concat($vNum, '|')" />
<xsl:if test="$vCurrItem/#match">
<xsl:value-of select="concat($vCurrItem/#match, '|')" />
</xsl:if>
</xsl:variable>
<!--Call template recursively to process the rest of the instances-->
<xsl:call-template name="ProcessItem">
<xsl:with-param name="pItemsList" select="$pItemsList[position() > 1]" />
<xsl:with-param name="pProcessedList" select="$vNewList" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="KEY">
<xsl:copy>
<xsl:copy-of select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:element name="Result">
<xsl:call-template name="ProcessItem">
<xsl:with-param name="pItemsList" select="//ITEM" />
<xsl:with-param name="pProcessedList" select="'|'" />
</xsl:call-template>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
IF there is only one match or none to each item you can give the following xslt a try:
<?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" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:key name="kItemNr" match="ITEM" use="#number" />
<xsl:key name="kNumberKey" match="KEY" use="concat(../#number, '|', #name )" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM">
<xsl:if test="not(preceding::ITEM[#number = current()/#match])" >
<Group>
<xsl:attribute name="number">
<xsl:value-of select="#number"/>
<xsl:if test="#match" >
<xsl:text>,</xsl:text>
<xsl:value-of select="#match"/>
</xsl:if>
</xsl:attribute>
<xsl:variable name="itemNr" select="#number"/>
<xsl:apply-templates select="KEY | key('kItemNr',#match )/KEY[
not (key('kNumberKey', concat($itemNr, '|', #name) ) )] ">
<xsl:sort select="#name"/>
</xsl:apply-templates>
</Group>
</xsl:if>
</xsl:template>
<xsl:template match="/" >
<Result>
<xsl:for-each select="//ITEM[count(. | key('kItemNr',number ) ) = 1 ]" >
<xsl:apply-templates select="." />
</xsl:for-each>
</Result>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
<?xml version="1.0"?>
<Result>
<Group number="1"/>
<Group number="2"/>
<Group number="3,5">
<KEY name="key1" value="x"/>
<KEY name="key2" value="y"/>
<KEY name="key3" value="z"/>
</Group>
<Group number="4"/>
<Group number="6,10">
<KEY name="key1" value="x"/>
<KEY name="key2" value="y"/>
<KEY name="key3" value="z"/>
<KEY name="key4" value="a"/>
<KEY name="key5" value="b"/>
</Group>
<Group number="7"/>
<Group number="8">
<KEY name="key1" value="x"/>
</Group>
<Group number="9"/>
</Result>
Update because of changed request:
<?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" indent="yes"/>
<xsl:key name="kItemNr" match="ITEM" use="#number" />
<xsl:template match="#*|node()">
<xsl:copy >
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ITEM">
<xsl:variable name="matchStr" select=" concat(',', current()/#match, ',')"/>
<xsl:if test="not(preceding::ITEM[ contains($matchStr, concat(',', #number, ',') )])" >
<Group>
<xsl:attribute name="number">
<xsl:value-of select="#number"/>
<xsl:if test="#match" >
<xsl:text>,</xsl:text>
<xsl:value-of select="#match"/>
</xsl:if>
</xsl:attribute>
<xsl:apply-templates select="(KEY |
//ITEM[
contains( $matchStr, concat(',', #number, ',') )
]/KEY[
not((preceding::ITEM[
contains( $matchStr, concat(',', #number, ',') )
] | current() )/KEY/#name = #name)
]) ">
<xsl:sort select="#name"/>
</xsl:apply-templates>
</Group>
</xsl:if>
</xsl:template>
<xsl:template match="/" >
<Result>
<xsl:for-each select="//ITEM[count(. | key('kItemNr',number ) ) = 1 ]" >
<xsl:apply-templates select="." />
</xsl:for-each>
</Result>
</xsl:template>
</xsl:stylesheet>
This may be quite slow for bigger input data but any way.

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.