How to remove empty lines in XSLT to CSV output - xslt

I have XML data which I have transformed using XSLT to output csv formatted text. My XSLT contains a <xsl:if test> clause to filter the result from the larger dataset of the input XML. This works but every line that is filtered out of the input is displayed as an empty line in the output.
I have tried <xsl:strip-space elements="*" /> and <xsl:template match="text()" /> but neither remove the empty lines.
A sample of my XML:
<?xml version="1.0" encoding="UTF-8"?>
<session_list xmlns="http://www.networkstreaming.com/namespaces/API" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<session lsid="fef672741e025ffda1acb3041f09252d">
<session_type>support</session_type>
<lseq>2899</lseq>
<start_time timestamp="1290027608">2010-11-17T16:00:08-05:00</start_time>
<end_time timestamp="1290027616">2010-11-17T16:00:16-05:00</end_time>
<duration>00:00:08</duration>
<public_site id="1">Default</public_site>
<external_key></external_key>
<session_chat_view_url>https://mysite.com/session_download.ns?lsid=l%3Dfef672741e025ffda1acb3041f09252d%3Bh%3D9bd6081f0b7fee08dcc32a58ef4cb54c7a0e233d%3Bt%3Dsd%3Bm%3Dchat&dl_action=chat&view=1&sessionType=sd</session_chat_view_url>
<session_chat_download_url>https://mysite.com/session_download.ns?lsid=l%3Dfef672741e025ffda1acb3041f09252d%3Bh%3D9bd6081f0b7fee08dcc32a58ef4cb54c7a0e233d%3Bt%3Dsd%3Bm%3Dchat&dl_action=chat&sessionType=sd</session_chat_download_url>
<file_transfer_count>0</file_transfer_count>
<primary_customer gsnumber="3">Smith, John</primary_customer>
<customer_list>
<customer gsnumber="3">
<username>Smith, John</username>
<public_ip>xxx.xxx.xxx.xxx</public_ip>
<private_ip>xxx.xxx.xxx.xxx</private_ip>
<hostname>DESKTOP</hostname>
<os>Windows 7 Enterprise x64 Edition (Build 7600)</os>
<primary_cust>1</primary_cust>
<info>
<name></name>
<company></company>
<company_code></company_code>
<issue></issue>
<details></details>
</info>
</customer>
</customer_list>
...etc...
</session>
</session_list>
My XSLT:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:bg="http://www.networkstreaming.com/namespaces/API" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:text>Session ID,</xsl:text>
<xsl:text>Start Time,</xsl:text>
<xsl:text>Duration,</xsl:text>
<xsl:text>Public Site,</xsl:text>
<xsl:text>Remedy Ticket Number,</xsl:text>
<xsl:text>Customer's Name,</xsl:text>
<xsl:text>Customer's Operating System,</xsl:text>
<xsl:text>Representative's Name</xsl:text>
<xsl:text>
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="bg:session_list">
<xsl:apply-templates select="bg:session"/>
</xsl:template>
<xsl:template match="bg:session">
<xsl:if test="contains(bg:customer_list/bg:customer/bg:os,'Click-To-Chat')">
<xsl:value-of select="bg:lseq"/>,<xsl:text/>
<xsl:value-of select="bg:start_time"/>,<xsl:text/>
<xsl:value-of select="bg:duration"/>,<xsl:text/>
<xsl:value-of select="bg:public_site"/>,<xsl:text/>
<xsl:value-of select="bg:external_key"/>,<xsl:text/>
<xsl:value-of select="bg:primary_customer"/>,<xsl:text/>
<xsl:value-of select="bg:customer_list/bg:customer/bg:os"/>,<xsl:text/>
<xsl:value-of select="bg:primary_rep"/>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
A sample of the output:
Session ID,Start Time,Duration,Public Site,Remedy Ticket Number,Customer's Name,Customer's Operating System,Representative's Name
6488,2011-03-14T09:17:13-04:00,00:00:06,Default,,User One,Windows® (x86) Click-To-Chat,Rep1
6489,2011-03-14T09:44:50-04:00,00:39:58,Default,,Nate,Windows® (x86) Click-To-Chat,Rep1
6494,2011-03-14T10:25:23-04:00,00:03:29,Default,,User TEST,Windows® (x86) Click-To-Chat,Rep1
6498,2011-03-14T11:01:36-04:00,,Default,,User Two,Windows® (x86) Click-To-Chat,Diane
Each of the lines between the printed lines is data that was filtered out because it didn't pass the <xsl:if test="contains(bg:customer_list/bg:customer/bg:os,'Click-To-Chat')"> but the empty lines are still printed. This output doesn't look very good when opened in excel.
Does anyone have any ideas how to remove them?
Thanks!

Have you tried putting the <xsl:text>
</xsl:text> line inside the xsl:if? This is the bit that puts a newline, so at the moment you are writing one even if you don't write the line.

Related

Text value of input xml element as final xslt output

I have a scenario where the input(source) xml is having an element which contains a valid well formed xml as string. I am trying to write an xslt that would give me the text value of that desired element which contains the payload xml. In essence, output should only be text of the element that contains it. Here is what I am trying, am I missing something obvious here. I am using xslt 1.0
Thanks.
Input xml:
<BatchOrders xmlns="http://Microsoft.ABCD.OracleDB/STMT">
<BatchOrdersRECORD>
<BatchOrdersRECORD>
<ActualPayload>
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
</ActualPayload>
</BatchOrdersRECORD>
</BatchOrdersRECORD>
</BatchOrders>
Xslt:
<?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="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
<xsl:template name="xml-to-string-called-template">
<xsl:param name ="param1">
<xsl:element name ="DestPayload">
<xsl:text disable-output-escaping ="yes"><![CDATA[</xsl:text>
<xsl:call-template name ="sourcecopy"/>
<xsl:text disable-output-escaping ="yes">]]></xsl:text>
</xsl:element>
</xsl:param>
</xsl:template>
</xsl:stylesheet>
Desired Output:
<PersonName>
<PersonGivenName>CaptainJack</PersonGivenName>
<PersonMiddleName>Walter</PersonMiddleName>
<PersonSurName>Sparrow</PersonSurName>
<PersonNameSuffixText>Sr.</PersonNameSuffixText>
</PersonName>
Do you really need the mode="xml-to-string"?
Change
<xsl:template match="text()|#*" name="sourcecopy" mode="xml-to-string">
<xsl:value-of select="*"/>
</xsl:template>
to
<xsl:template match="text()|#*" name="sourcecopy">
<xsl:value-of select="." disable-output-escaping ="yes"/>
</xsl:template>
Would this template suffice?

Setting disable-output-escaping="yes" for every xsl:text tag in the xml

say I have the following xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<xsl:for-each select="logline_t">
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_1" <xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_2" <xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_3" <xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:for-each>
</display>
</xsl:template>
</xsl:stylesheet>
Is there a way to set disable-output-escaping="yes" to all of the xsl:text that appear in the document?
I know there is an option to put
< xsl:output method="text"/ >
and every time something like
& lt;
appears, a < will appear, but the thing is that sometimes in the values of line_1, line_2 or line_3, there is a "$lt;" that I don't want changed (this is, I only need whatever is between to be changed)
This is what I'm trying to accomplish. I have this xml:
<readlog_l>
<logline_t>
<hora>16:01:09</hora>
<texto>Call-ID: 663903<hola>396#127.0.0.1</texto>
</logline_t>
</readlog_l>
And this translation:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<screen name="<xsl:value-of select="name(.)"/>">
<xsl:for-each select="logline_t">
< field name="<xsl:for-each select="*"><xsl:value-of select="."/></xsl:for-each>" value="" type="label"/>
</xsl:for-each>
</screen>
</display>
</xsl:template>
</xsl:stylesheet>
I want this to be the output:
<?xml version="1.0"?>
<display>
<screen name="readlog_l">
<field name="16:01:09 Call-ID: 663903<hola>396#127.0.0.1 " value="" type="label">
</screen>
</display>
Note that I need the "<" inside the field name not to be escaped, this is why I can't use output method text.
Also, note that this is an example and the translations are much bigger, so this is why I'm trying to find out how not to write disable-output-escaping for every '<' or '>' I need.
Thanks!
Thanks for clarifying the question. In this case, I'm fairly sure there's no need to disable output escaping. XSLT was designed to accomplish what you're doing:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<screen name="{name(.)}">
<xsl:for-each select="logline_t">
<xsl:variable name="nameContent">
<xsl:for-each select="*">
<xsl:if test="position() > 1"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:variable>
<field name="{$nameContent}" value="" type="label" />
</xsl:for-each>
</screen>
</display>
</xsl:template>
</xsl:stylesheet>
I'm a bit unclear on this point:
Note that I need the "<" inside the field name not to be escaped, this is why I can't use output method text.
Which < are you referring to? Is it the < and > around "hola"? If you left those unescaped you would wind up with invalid XML. It also looks like the name attribute in your sample output have a lot of values that aren't in the input XML. Where did those come from?
Given your expected output you don't need d-o-e at all for this. Here is a possible solution that doesn't use d-o-e, and is based on templates rather than for-each:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<display>
<screen name="{name(.)}">
<xsl:apply-templates select="logline_t"/>
</screen>
</display>
</xsl:template>
<xsl:template match="logline_t">
<field value="" type="label">
<xsl:attribute name="name">
<xsl:apply-templates select="*" mode="fieldvalue"/>
</xsl:attribute>
</field>
</xsl:template>
<xsl:template match="*[last()]" mode="fieldvalue">
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="*" mode="fieldvalue">
<xsl:value-of select="." />
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
If you want to set d-o-e on everything, that suggests you are trying to generate markup "by hand". I don't think that's a particularly good idea (in fact, I think it's a lousy idea), but if it's what you want to do, I would suggest using the text output method instead of the xml output method. That way, no escaping of special characters takes place, and therefore it doesn't need to be disabled.

Trouble with xsl:for-each

I must be missing some fundamental concept of processing an XML document. Here is my source XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Root>
<Element>visitorNameAlt</Element>
<Element>visitorScore</Element>
<Element>visitorTimeouts</Element>
<Element>Blank</Element>
<Element>homeNameAlt</Element>
<Element>homeScore</Element>
<Element>homeTimeouts</Element>
<Element>Blank</Element>
<Element>period</Element>
<Element>optionalText</Element>
<Element>flag</Element>
<Element>Blank</Element>
<Element>scoreLogo</Element>
<Element>sponsorLogo</Element>
</Root>
And my XSL stylesheet:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="/Root">
<xsl:value-of select="position()"/>
<xsl:value-of select="Element"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
All I want is to pluck the "Element" names from the source XML doc with their relative position in front.
My output is just "1" followed by the first element and nothing more.
I am new to XSLT, but have processed other documents successfully with for-each.
Thanks in advance.
Bill
You're looping over Root tags, not Element tags. Try this:
<xsl:template match="/">
<xsl:for-each select="/Root/Element">
<xsl:value-of select="position()"/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
Note that you must change the second value-of select to "." or "text()".
XSLT is not an imperative programming language. The XSLT processor grabs each element in turn and tries to match it to your stylesheet. The idiomatic way to write this is without a for-each:
<xsl:template match="/Root">
<xsl:apply-templates select="Element"/>
</xsl:template>
<xsl:template match="Element">
<xsl:value-of select="position()"/>
<xsl:value-of select="."/>
</xsl:template>
The first template matches the root and tells the processor to apply the stylesheet to all the Element nodes inside the Root. The second template matches those nodes, and outputs the desired information.

How to select element name, not value, using XSL/XSLT?

We are using XSL to convert a XML file into a pipe-delimited format.
<?xml version="1.0" encoding="UTF-8"?>
<ns:tradedata xmlns:ns="http://schemas.com/enterprise/util/extractservice/v1">
<tradedata_item>
<ORDER_ID>113632428</ORDER_ID>
<CUSIP>31393FHA7</CUSIP>
<TICKER>FHR</TICKER>
<SEC_NAME>FHR 2527 SG</SEC_NAME>
<ORDER_QTY>169249.6824</ORDER_QTY>
</tradedata_item>
<tradedata_item>
<ORDER_ID>113632434</ORDER_ID>
<CUSIP>31393G2C7</CUSIP>
<TICKER>FHR</TICKER>
<SEC_NAME>FHR 2531 ST</SEC_NAME>
<ORDER_QTY>214673.0105</ORDER_QTY>
</tradedata_item>
<tradedata_item>
<ORDER_ID>113632431</ORDER_ID>
<CUSIP>527069AH1</CUSIP>
<TICKER>LESL</TICKER>
<SEC_NAME>ZZZ_LESLIE S POOLMART INC</SEC_NAME>
<ORDER_QTY>365000.0000</ORDER_QTY>
</tradedata_item>
</ns:tradedata>
We need the first line in the output to be the column headers, and everything else would be data, like this...
ORDER_ID|CUSIP|TICKER|SEC_NAME|ORDER_QTY
1136324289|31393FHA7|FHR|FHR 2527 SG|169249.6824
1136324304|31393G2C7|FHR|FHR 2531 ST|214673.0105
We've got the XSL working to get the data, but we can't get the header to output correctly. We just select the first tradedata_item element, then iterate the element name and separate them using | characters. Here is the full XSL...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl"
version="1.0" xmlns="http://schemas.com/enterprise/util/extractservice/v1"
xmlns:o="http://schemas.com/enterprise/util/extractservice/v1" >
<!-- xsl:strip-space elements="*"/-->
<xsl:output method="text" indent="no"/>
<xsl:template match="/tradedata/tradedata_item[1]">
<xsl:for-each select="*">
<xsl:value-of select="local-name()"/>|
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="tradedata/tradedata_item">
<xsl:value-of select="ORDER_ID"/>|<xsl:value-of select="CUSIP"/>|<xsl:value-of select="TICKER"/>|<xsl:value-of select="SEC_NAME"/>|<xsl:value-of select="ORDER_QTY"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output we're seeing is just data, no header...
113632428|31393FHA7|FHR|FHR 2527 SG|169249.6824
113632430|31393G2C7|FHR|FHR 2531 ST|214673.0105
113632431|527069AH1|LESL|ZZZ_LESLIE S POOLMART INC|365000.0000
113632434|38470RAD3|GRAHAM|ZZZ_GRAHAM PACKAGING CO|595000.0000
Please disregard any namespace inconsistencies; I had to obfuscate the xml and xsl for legal reasons.
Try this :
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="tradedata/tradedata_item[1]/*">
<xsl:value-of select="concat(name(), '|')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output :
ORDER_ID|CUSIP|TICKER|SEC_NAME|ORDER_QTY|
It seems pretty simple to me. Maybe your error lies elsewhere.
I tried your code.. I only changed the first template to match:
<xsl:template match="//tradedata_item[1]">
and it worked for me, i.e. got the header names.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl ofi"
version="1.0" xmlns="http://schemas.com/enterprise/util/extractservice/v1"
xmlns:ofi="http://schemas.oppen.com/enterprise/util/extractservice/v1">
<xsl:output method="text" indent="no"/>
<xsl:template match="tradedata_item[position()='1']">
<xsl:for-each select="self::*">
<xsl:for-each select="child::*[position()!='5']">
<xsl:value-of select="local-name(self::*)"/>|
</xsl:for-each>
<xsl:value-of select="local-name(child::*[position()='5'])"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="ORDER_ID"/>|<xsl:value-of select="CUSIP"/>|<xsl:value-of select="TICKER"/>|<xsl:value-of select="SEC_NAME"/>|<xsl:value-of select="ORDER_QTY"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="tradedata_item[position()>1]">
<xsl:for-each select="self::*">
<xsl:value-of select="ORDER_ID"/>|<xsl:value-of select="CUSIP"/>|<xsl:value-of select="TICKER"/>|<xsl:value-of select="SEC_NAME"/>|<xsl:value-of select="ORDER_QTY"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

SSIS XSLT Transform

I am using SSIS on SQL Server 2005, VS 2005 SP2.
I have created an XSLT to convert XML to CSV format. When I run this through XML Spy it works fine. I have configured an XML Task within and SSIS package to transform the XML file. Unfortunately, when SSIS performs the transform it does not include the CR/LF at the end of each record - I am left with a sigle line.
I have pased the XSLT below. Could anyone let me know why SSIS is ignoring the 
?
I have also pasted a sample XML doc.
Many thanks,
Rob.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/pptxn">
<xsl:variable name="fileName" select="/pptxn/file_name"/>
<xsl:variable name="sendingOrg" select="/pptxn/sending_org"/>
<xsl:variable name="dateCreated" select="/pptxn/date_created"/>
<xsl:variable name="timeCreated" select="/pptxn/time_created"/>
<xsl:variable name="sequenceNumber" select="/pptxn/sequence_number"/>
<xsl:text>FileName,SendingOrg,DateCreated,TimeCreated,SequenceNumber,PartnerNumber,PartnerOutletRef,CardAccountNumber,TransactionDate,TransactionTime,Spend,PartnerPoints,PartnerReference,PartnerPosId</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="transaction">
<xsl:value-of select="$fileName"/>,<xsl:value-of select="$sendingOrg"/>,<xsl:value-of select="$dateCreated"/>,<xsl:value-of select="$timeCreated"/>,<xsl:value-of select="$sequenceNumber"/>,<xsl:value-of select="partner_number"/>,<xsl:value-of select="partner_outlet_ref"/>,<xsl:value-of select="card_account_number"/>,<xsl:value-of select="transaction_date"/>,<xsl:value-of select="transaction_time"/>,<xsl:value-of select="spend"/>,<xsl:value-of select="partner_points"/>,<xsl:value-of select="partner_reference"/>,<xsl:value-of select="partner_pos_id"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Now the XML -->
<?xml version="1.0" encoding="UTF-8"?>
<pptxn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Y:\PartnerPoints\XML\PointsPartnerRequest.xsd">
<file_name>PPRequest.xml</file_name>
<sending_org>1</sending_org>
<date_created>20091121</date_created>
<time_created>153421</time_created>
<sequence_number>2</sequence_number>
<transaction_count>3</transaction_count>
<transaction>
<partner_number>1</partner_number>
<partner_outlet_ref>outlet ref 1</partner_outlet_ref>
<card_account_number>1</card_account_number>
<transaction_date>20091221</transaction_date>
<transaction_time>091256</transaction_time>
<spend>21.34</spend>
<partner_points>40</partner_points>
<partner_reference>shop x1</partner_reference>
<partner_pos_id>pos id 1</partner_pos_id>
</transaction>
<transaction>
<partner_number>2</partner_number>
<partner_outlet_ref>outlet ref 2</partner_outlet_ref>
<card_account_number>2</card_account_number>
<transaction_date>20091222</transaction_date>
<transaction_time>091257</transaction_time>
<spend>21.35</spend>
<partner_points>41</partner_points>
<partner_reference>shop x2</partner_reference>
<partner_pos_id>pos id 2</partner_pos_id>
</transaction>
<transaction>
<partner_number>3</partner_number>
<partner_outlet_ref>outlet ref 3</partner_outlet_ref>
<card_account_number>3</card_account_number>
<transaction_date>20091223</transaction_date>
<transaction_time>091258</transaction_time>
<spend>21.36</spend>
<partner_points>42</partner_points>
<partner_reference>shop x3</partner_reference>
<partner_pos_id>pos id 3</partner_pos_id>
</transaction>
</pptxn>
You may need to change
<xsl:text>
</xsl:text>
to
<xsl:text>
</xsl:text>
I changed the char code from hex to decimal and now it works on SSIS 2005 :)
<xsl:text>
</xsl:text>
very strange...