An empty line in csv output file using xslt - java

Hi i am have an XML file and trying to convert it using xslt. But issue is i get a white line before the output.How do i eliminate it.
Below is my XML
<?xml version="1.0" encoding="UTF-8"?>
<UserInfo xmlns="http://XXXXXX">
<User>
<UserName>MNO</UserName>
<Userid>1234</Userid>
<address>xyz</address>
<city>ABC</city>
<state>XX</state>
<zip>000000</zip>
</User>
<User>
<UserName>DEF</UserName>
<Userid>4567</Userid>
<address>IJK</address>
<city>GHI</city>
<state>XX</state>
<zip>000000</zip>
</User>
</UserInfo>
below is my xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" >
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
UserName,Userid,address,city,state,zip
<xsl:for-each select="//User">
<xsl:value-of select="concat(UserName,',',Userid,',',address,',',city,',',state,',',zip,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When ever i run the above i get a white line first and then the output.
vendorName,vendorId,vendorTaxId,addressLine1,addressLine2,city,state,zip
MNO,1234,xyz,ABC,XX,000000
DEF,4567,IJK,GHI,XX,000000
I get the white space above the nodes(UserName,UserId etc)

Use xsl:text to output literal text, e.g.:
<xsl:template match="/">
<xsl:text>UserName,Userid,address,city,state,zip
</xsl:text>
<xsl:for-each select="//User">
<xsl:value-of select="concat(UserName,',',Userid,',',address,',',city,',',state,',',zip,'
')"/>
</xsl:for-each>
</xsl:template>
The way you have it now, the entire text node is written to the output - including the line break between <xsl:template match="/"> and UserName.

Related

How to convert complex Xml to csv?

I am writing a program in java( pre-junior), I really need help with xslt transformation. It is necessary to make a csv file from xml.
I got this xslt filter:
<?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" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="node()" name="conv">
<xsl:call-template name="loop"/>
</xsl:template>
<xsl:template name="loop">
<xsl:for-each select="./*[count(*) = 0]">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:if test="position() = last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="./*[(count(*) != 0) and (name()!='PARAMETRS')] ">
<xsl:call-template name="loop"/>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Source xml:
<Integration>
<PARAMETRS>
<ID>AZD</ID>
<DATE>2020-01-01</DATE>
</PARAMETRS>
<ORG>
<Thing>
<object>10220</object>
<type>U</type>
<dyn>
<items>
<val>988009</val>
<datebegin>2019-12-12</datebegin>
</items>
</dyn>
</Thing>
<Thing>
<object>10221</object>
<type>U</type>
<dyn>
<items>
<val>988010</val>
<datebegin>2019-12-13</datebegin>
</items>
<items>
<val>988011</val>
<datebegin>2019-12-14</datebegin>
</items>
</dyn>
</Thing>
</ORG>
</Integration>
In the output, I get comma-separated lines, and a few more lines (those same items) with the values below. and can't figure out how to concatenate the values ...
I would do it via value-of select = "concat" but my may have several dyn (1, 2, 3 ...), hence this is not suitable.
The output needs a csv separated by commas.
Please advise how to concatenate the item with its parent? Or there are simpler ways to parse xml with a different number of subsections(childs).
Expected output:
10220,U,988009,2019-12-12
10221,U,988010,2019-12-13,988011,2019-12-14
The output you show can be easily obtained using the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:for-each select="ORG/Thing">
<xsl:value-of select="object"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="type"/>
<xsl:text>,</xsl:text>
<xsl:for-each select="dyn/items">
<xsl:value-of select="val"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="datebegin"/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note that the output has a set of columns for each items; this is not an ideal CSV structure.
If you can use XSLT 2.0, it opens up new powerful functionality.
Oracle XML Developer Kit (XDK) supports XSLT 2.0
Here is the link: Using the XSLT Processor for Java
The approach below is doing the following:
Using string-join() function to concatenate all child elements values
on a different hierarchy level via .//*/(text()[1] expression.
xs:token casting removes white spaces.
XPath predicate [. != ''] removes empty sequence members.
XSLT 2.0
<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:for-each select="ORG/Thing">
<xsl:value-of select="string-join((.//*/(text()[1] cast as xs:token?))[. != ''],',')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output
10220,U,988009,2019-12-12
10221,U,988010,2019-12-13,988011,2019-12-14
Based on the Marting Honnen great tip, here is even more concise XSLT 2.0 version without any loop.
XSLT 2.0
<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/Integration">
<xsl:value-of select="ORG/Thing/string-join((.//*/(text()[1] cast as xs:token?))[. != ''],',')" separator="
"/>
</xsl:template>
</xsl:stylesheet>

XSL. Passing param in string

Here is my XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="RSA-InsurerID"/>
<xsl:param name="RSA-schema-version"/>
<xsl:template match="/">
<rsa:DriverStatusRequest xmlns:rsa="com/rsa/eosago/schema-"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InsurerID>
<xsl:value-of select="$RSA-InsurerID"
xmlns:ns2="com/rsa/eosago/schema-"/>
</InsurerID>
<IDCheckDriver>
<xsl:value-of select="ns2:DriverResponse/IDCheckDriver"
xmlns:ns2="com/rsa/eosago/schema-"/>
</IDCheckDriver>
</rsa:DriverStatusRequest>
</xsl:template>
These two params values are passed via Apache Camel.
The question is how to pass and concat the param
<xsl:param name="RSA-schema-version"/>
with xmlns:rsa="com/rsa/eosago/schema-" ?
I got my <xsl:param name="RSA-InsurerID"/> with <xsl:value-of select="$RSA-InsurerID", but i have no idea how to pass it to the value text.
I expect this output:
<?xml version="1.0" encoding="UTF-8"?>
<rsa:DriverStatusRequest xmlns:rsa="com/rsa/eosago/schema-1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InsurerID>18800000</InsurerID>
<IDCheckDriver/>
</rsa:DriverStatusRequest>
Big thanks!
Seems what you try to do is to generate a namespaces dynamically at run time.
For example have a look to this or this answers.
And try:
<xsl:template match="/">
<xsl:element name="rsa:DriverStatusRequest" namespace="com/rsa/eosago/schema-{$RSA-schema-version}" >
<InsurerID>
<xsl:value-of select="$RSA-InsurerID" />
</InsurerID>
</xsl:element>
</xsl:template>
Which will generate:
<rsa:DriverStatusRequest xmlns:rsa="com/rsa/eosago/schema-1.2">
<InsurerID>18800000</InsurerID>
</rsa:DriverStatusRequest>
But I assume there will be loot more problems coming up.
Try
<InsurerID>
<xsl:value-of select="$RSA-InsurerID"
xmlns:ns2="com/rsa/eosago/schema-{$RSA-schema-version}"/>

Parsing nested XML File as node set

I have a problem. I have an xml file as below :
Main.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<msrsw>
<software>
<chapter>
<name> Hello world</sample>
<xref type="xml">C:\ABC\NestedXML.xml</xref>
</chapter>
</software>
</msrsw>
NestedXml.xml :
<?xml version="1.0" encoding="ISO-8859-1"?>
<msrsw>
<software>
<chapter>
<name> Nested XML </sample>
<age>14</age>
<country>Canada</country>
</chapter>
</software>
</msrsw>
I am using Apache FOP to produce PDF document (Using Java). FOP needs src and dest params. Src being Main.xml.
My XSLT is as below:
<xsl:param name="xmlFileName" />
<xsl:param name="XMLFile" select="document($xmlFileName)"/>
<xsl:template name="Chapters_1_2_Template">
<xsl:apply-templates select="$XMLFile/*" mode="chapter" />
</xsl:template>
<xsl:template match="node()" mode="chapter">
<xsl:for-each select="node()">
<xsl:if test="current()[name() = 'xref']">
<xsl:apply-templates select="current()[name() = 'xref']" mode="x" />
</xsl:if>
<xsl:if test="current()[name() = 'name']">
<xsl:apply-templates select="current()[name() = 'name']" mode="name" />
</xsl:if>
<xsl:if test="current()[name() = 'age']">
<xsl:apply-templates select="current()[name() = 'age']" mode="age" />
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="name" mode="name">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="age" mode="age">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="country" mode="country">
<xsl:value-of select="current()" />
</xsl:template>
<xsl:template match="xref" mode="x">
<xsl:param name="XMLFile" select="document(current())"/>
<xsl:call-template name="Chapters_1_2_Template" />
</xsl:template>
When I execute the above code, i get the following error in the line <xsl:apply-templates select="$XMLFile/*" mode="chapter" />:
Invalid token XMLFile.
When I remove $XMLFile - <xsl:apply-templates select="*" mode="chapter" /> , it works fine for me with Main.xml file's content.
When I just display the value of current() in xref template, it is parses the NestedXml.xml file and displayes string values in PDF.
Expected Output is:
Hello world
Nested XML
14
Canada
My requirement is to embed the node-set of NestedXML.xml within the<xref></xref> tags and recursively applyTemplates on that node-set.
But the document() function gives me the parsed String content of NestedXml.xml.
I do not want to use the document() function and just get the String value of NestedXml.xml. I need to parse all the tags of NestedXml.xml using the same XSLT.
Please suggest me where am I going wrong. Is this approach right? Is there any other way to do this?
Or it is not possible to do in this way using XSLT and that XSLT allows including only the parsed String values?
If it is that you want just some text nodes, the stylesheet needn't be that complex. Try this simpler stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="msrsw/software/chapter/name"/>
<xsl:apply-templates select="document(msrsw/software/chapter/xref)/*"/>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

Splitting an xml based on the contents of xml either using java or xslt

I have a requirement,consider the below xml data
Input1.xml
<Envelope>
<Notification>
<Data>
<Input>ABCDEFGHIJKLMN</Input>
<Output>RESPONSEDATA</Output>
</Data>
<Data>
<Input>OPQRSTUVWXYZ</Input>
<Output>NEXTDATA</Output>
</Data>
<Data>
<Input>ALPHABETS</Input>
<Output>SOMEDATA</Output>
</Data>
</Notification>
</Envelope>
Now I want 3 output xmls with the response as shown below the file name to have first 6 characters ofABCDEFGHIJKLMN as output file name to have as shown below
(FILE1)->ABCDEF.XML
<Output>RESPONSEDATA</Output>
(FILE2)->OPQRST.XML
<Output>NEXTDATA</Output>
(FILE3)->ALPHAB.XML
<Output>SOMEDATA</Output>
Which XSLT 1.0 processor do you use? Xalan Java supports
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:redirect="http://xml.apache.org/xalan/redirect"
extension-element-prefixes="redirect"
exclude-result-prefixes="redirect">
<xsl:template match="/Envelope/Notification/Data[not(Input/*)]">
<redirect:write select="concat(substring(Input, 1, 6), '.xml')">
<xsl:copy-of select="Output"/>
</redirect:write>
</xsl:template>
<xsl:template match="/Envelope/Notification/Data[Input/*]">
<redirect:write select="concat(local-name(Input/*), '.xml')">
<xsl:copy-of select="Output"/>
</redirect:write>
</xsl:template>
</xsl:stylesheet>
Use the solution described in Your Previous Question you can then create 3 xslt files as follow, and apply the same xml input one for each xslt/output. Explain:
<xsl:copy-of select="/Envelope/Notification/Data[child::Output/text()='RESPONSEDATA']"/>
is saying, copy all "/Envelope/Notification/Data" and it content, but only for the child Output with a text value of 'RESPONSEDATA'.
ExtractResponseData.xslt
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="/">
<xsl:copy-of select="/Envelope/Notification/Data[child::Output/text()='RESPONSEDATA']"/>
</xsl:template>
</xsl:stylesheet>
ExtractNextData.xslt
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="/">
<xsl:copy-of select="/Envelope/Notification/Data[child::Output/text()='NEXTDATA']"/>
</xsl:template>
</xsl:stylesheet>
ExtractSomeData.xslt
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="/">
<xsl:copy-of select="/Envelope/Notification/Data[child::Output/text()='SOMEDATA']"/>
</xsl:template>
</xsl:stylesheet>

Mapping two sources to one output

I want to performs XSLT on two sources.
Sourch_1.xml:
<Root>
<record>
<PolicyNumber>1</PolicyNumber>
<FirdsName>aa</FirdsName>
<LastName>aa</LastName>
</record>
<record>
<PolicyNumber>2</PolicyNumber>
<FirdsName>bb</FirdsName>
<LastName>bb</LastName>
</record>
Sourch_2.xml:
<Root>
<record>
<policy>
<PolicyNumber>1</PolicyNumber>
<city>aaCity</city>
<street>aaStreet</street>
</policy>
<policy>
<PolicyNumber>2</PolicyNumber>
<city>bbCity</city>
<street>bbStreet</street>
</policy>
</record>
I want the output to create tags which combine data from two sources based on PolicyNumber.
I want my output to be:
output.xml:
<Root>
<record_data>
<PolicyNumber>1</PolicyNumber>
<FirdsName>aa</FirdsName>
<LastName>aa</LastName>
<city>aaCity</city>
<street>aaStreet</street>
</record_data>
<record_data>
<PolicyNumber>2</PolicyNumber>
<FirdsName>bb</FirdsName>
<LastName>bb</LastName>
<city>bbCity</city>
<street>bbStreet</street>
</record_data>
How can i accomplish that using XSLT?
To process more than one XML input file, use the document() function. Make sure that additional XML files are in the folder where you put the XSLT stylesheet, too.
The line that deserves your attention is the following:
<xsl:for-each select="document('double2.xml')/root/record/policy[./PolicyNumber=current()/PolicyNumber]">
To begin with, document('double2.xml') opens the second XML file (I have dubbed it "double2.xml"). For the policy elements in this second XML file it checks whether their PolicyNumber is equal to the PolicyNumber of the record element from the first XML file that is being processed
I have slightly modified your input to make it well-formed (lowercased the root element and added its closing tag). Note that there is also a typo in it and you probably meant to write "FirstName" instead of "FirdsName".
Stylesheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="record">
<record_data>
<xsl:copy-of select="*"/>
<xsl:for-each select="document('double2.xml')/root/record/policy[./PolicyNumber=current()/PolicyNumber]">
<xsl:copy-of select="city|street"/>
</xsl:for-each>
</record_data>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<record_data>
<PolicyNumber>1</PolicyNumber>
<FirdsName>aa</FirdsName>
<LastName>aa</LastName>
<city>aaCity</city>
<street>aaStreet</street>
</record_data>
<record_data>
<PolicyNumber>2</PolicyNumber>
<FirdsName>bb</FirdsName>
<LastName>bb</LastName>
<city>bbCity</city>
<street>bbStreet</street>
</record_data>
</root>
Something like below will work for you.
NOTE : I have not ran this code. where I check not(policy) you might do some change in xpath like not(./policy)
<?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="/">
<xsl:for-each select="Root/record">
<xsl:choose>
<xsl:when test="not(policy)">
<record_data>
<PolicyNumber><xsl:value-of select="PolicyNumber"/></PolicyNumber>
<FirdsName><xsl:value-of select="FirdsName"/></FirdsName>
<LastName><xsl:value-of select="LastName"/></LastName>
<city><xsl:value-of select="city"/></city>
<street><xsl:value-of select="street"/></street>
</record_data>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
<record_data>
<PolicyNumber><xsl:value-of select="policy/PolicyNumber"/></PolicyNumber>
<FirdsName><xsl:value-of select="policy/FirdsName"/></FirdsName>
<LastName><xsl:value-of select="policy/LastName"/></LastName>
<city><xsl:value-of select="policy/city"/></city>
<street><xsl:value-of select="policy/street"/></street>
</record_data>
</xsl:choose>
</xsl:for-each>

Categories