7

I am are trying to convert an CSV (comma separated file) into XML. For this, I am coding an XSLT template and this is my 1st try at XSLT...

CSV sample:

ClaimRef,HandlerRef,ClaimType,Date,Area,SettleDate,ClaimStatus,ClaimantName
1,1/1,Liability,08-12-2013,US,23-05-2014,Closed,Mark
2,1/2,Liability,08-10-2013,UK,23-02-2014,Closed,John

Desired XML Output format:

 <Claims>
     <Claim>
      <ClaimRef></ClaimRef>
      <HandlerRef></HandlerRef>
      <ClaimType></ClaimType>
      <Date></Date>
      <Area></Area>
      <SettleDate></SettleDate>
      <ClaimStatus></ClaimStatus>
      <ClaimantName></ClaimantName>
     </Claim>
    </Claims>

I used http://blogs.msdn.com/b/kaevans/archive/2003/04/17/5780.aspx as initial start and http://xslttest.appspot.com/ to test the results. But this article mentions how to get the values as <row><elem>, etc.

Please can you guide me how to code an XSLT to generate above XML based on sample CSV data.

DanMan
  • 10,986
  • 4
  • 38
  • 58
onkarsahas
  • 240
  • 1
  • 2
  • 9
  • Related: http://stackoverflow.com/questions/26965790/how-to-convert-csv-file-to-xml-using-xslt-1-0 – DanMan Aug 16 '15 at 20:57

1 Answers1

7

Here's an XSLT 2.0 option...

CSV Input (so.csv referenced in the csv-uri param.)

ClaimRef,HandlerRef,ClaimType,Date,Area,SettleDate,ClaimStatus,ClaimantName
1,1/1,Liability,08-12-2013,US,23-05-2014,Closed,Mark
2,1/2,Liability,08-10-2013,UK,23-02-2014,Closed,John

XSLT 2.0 (Use either a well-formed dummy XML doc or the stylesheet itself as input or specify csv2xml as the initial template.)

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="csv-encoding" as="xs:string" select="'iso-8859-1'"/>
    <xsl:param name="csv-uri" as="xs:string" select="'file:///C:/Users/dhaley/Desktop/so.csv'"/>

    <xsl:template match="/" name="csv2xml">
        <Claims>
            <xsl:choose>
                <xsl:when test="unparsed-text-available($csv-uri, $csv-encoding)">
                    <xsl:variable name="csv" select="unparsed-text($csv-uri, $csv-encoding)"/>
                    <!--Get Header-->
                    <xsl:variable name="header-tokens" as="xs:string*">
                        <xsl:analyze-string select="$csv" regex="\r\n?|\n">
                            <xsl:non-matching-substring>
                                <xsl:if test="position()=1">
                                    <xsl:copy-of select="tokenize(.,',')"/>                                        
                                </xsl:if>
                            </xsl:non-matching-substring>
                        </xsl:analyze-string>
                    </xsl:variable>                    
                    <xsl:analyze-string select="$csv" regex="\r\n?|\n">
                        <xsl:non-matching-substring>
                            <xsl:if test="not(position()=1)">
                                <Claim>
                                    <xsl:for-each select="tokenize(.,',')">
                                        <xsl:variable name="pos" select="position()"/>
                                        <xsl:element name="{$header-tokens[$pos]}">
                                            <xsl:value-of select="."/>
                                        </xsl:element>
                                    </xsl:for-each>
                                </Claim>
                            </xsl:if>
                        </xsl:non-matching-substring>
                    </xsl:analyze-string>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:variable name="error">
                        <xsl:text>Error reading "</xsl:text>
                        <xsl:value-of select="$csv-uri"/>
                        <xsl:text>" (encoding "</xsl:text>
                        <xsl:value-of select="$csv-encoding"/>
                        <xsl:text>").</xsl:text>
                    </xsl:variable>
                    <xsl:message><xsl:value-of select="$error"/></xsl:message>
                    <xsl:value-of select="$error"/>
                </xsl:otherwise>
            </xsl:choose>
        </Claims>
    </xsl:template>

</xsl:stylesheet>

XML Output

<Claims>
   <Claim>
      <ClaimRef>1</ClaimRef>
      <HandlerRef>1/1</HandlerRef>
      <ClaimType>Liability</ClaimType>
      <Date>08-12-2013</Date>
      <Area>US</Area>
      <SettleDate>23-05-2014</SettleDate>
      <ClaimStatus>Closed</ClaimStatus>
      <ClaimantName>Mark</ClaimantName>
   </Claim>
   <Claim>
      <ClaimRef>2</ClaimRef>
      <HandlerRef>1/2</HandlerRef>
      <ClaimType>Liability</ClaimType>
      <Date>08-10-2013</Date>
      <Area>UK</Area>
      <SettleDate>23-02-2014</SettleDate>
      <ClaimStatus>Closed</ClaimStatus>
      <ClaimantName>John</ClaimantName>
   </Claim>
</Claims>

Update per comment...

Here is the same example, but using a variable instead of an external CSV file. You can use this XSLT to test in other online test tools that support XSLT 2.0.

Example on xsltransform.net

Daniel Haley
  • 49,094
  • 5
  • 67
  • 90
  • Can you share the reference of your answer please. – variable Mar 27 '15 at 08:20
  • @variable - I'm sorry I'm not sure what you mean by share the reference. – Daniel Haley Mar 27 '15 at 08:25
  • Any reference article for this please. – variable Mar 27 '15 at 08:31
  • 1
    @variable - I do not have any reference articles. If you have any questions or would like me to add any additional details on specific pieces of the answer, please let me know. – Daniel Haley Mar 27 '15 at 08:34
  • I see that in the answer you are referring to the file:///C:/Users/dhaley/Desktop/so.csv; So how do I test it via tool like http://xslttest.appspot.com/. Please advice :) – variable Mar 27 '15 at 08:38
  • 1
    @variable - Oh I see. My answer is specific to an external .csv file. To test without an external file, you could add a variable and remove everything related to `unparsed-text()`. I will update my answer. – Daniel Haley Mar 27 '15 at 08:48
  • @variable - I've updated my answer with an example that uses http://xsltransform.net. You can use this at xslttest.appspot.com as well (I tested it there too). – Daniel Haley Mar 27 '15 at 08:54
  • The question remains - why use XSLT for that? It is clearly not the right choice of tool. CSV parsers exist in a host of more appropriate languages – Tomalak Mar 27 '15 at 08:56
  • @DanielHaley can you provide the XSL without the data please. I am looking for a generic template without data in the XSL itself. – variable Mar 27 '15 at 08:58
  • @Tomalak in .NET what do you suggest? I can across http://www.thescarms.com/dotnet/XSLT.aspx, but I need an generic XSLT template (without data within it. just the XSLT) – variable Mar 27 '15 at 08:59
  • @Tomalak - I missed that part of the question. How can you say that XSLT is clearly not the right choice of tool based on the minimal detail in the original question? If the csv isn't complex, why not? I've used this approach multiple times to get an initial conversion from CSV to XML that I can then perform complex transformations on (within the same XSLT). – Daniel Haley Mar 27 '15 at 09:04
  • XSLT is a language for processing XML. Use any general-purpose programming language you like for processing CSV. Which one depends on your experience. – Tomalak Mar 27 '15 at 09:06
  • @variable - The data is in the XSLT as an example. If the data is removed how do you expect to test it on your suggested xslttest.appspot.com? What you could also do is change the `xsl:variable` to an `xsl:param` and pass in the data at runtime. You'll have to try it to see if it works. Also, be sure you're using a 2.0 processor in .net. – Daniel Haley Mar 27 '15 at 09:07
  • @DanielHaley: On the website http://xslttest.appspot.com/ there are 2 boxes. The one on top is for data. And one below is for XSL. I put my CSV data in the top box. I need only the XSL that I can try in the 2nd box. How do I use the xsl:param for this? Please guide – variable Mar 27 '15 at 09:10
  • 2
    @Tomalak - Yes the main goal of XSLT is processing XML, but with functions such as `unparsed-text()` you are not strictly limited to processing only XML. This is very convenient when your target output is XML. – Daniel Haley Mar 27 '15 at 09:10
  • @Daniel CSV is complex enough (quoting, in-value linebreaks, escaping, nested quotes) to necessitate a parser. It is out of XSLTs domain to process CSV, even if you can bang on it long enough to make it work. It's like hammering in a nail with a bottle. Can be done, wrong tool nonetheless. I simply doubt the OP has made up his mind about why he chose XSLT. – Tomalak Mar 27 '15 at 09:11
  • @Tomalak I came across http://thescarms.com/dotnet/XSLT.aspx which shows how to convert CSV to XML via an XSLT template using .NET resolver class... Hence I am looking for a generic XSL for this. – variable Mar 27 '15 at 09:13
  • @variable - The top box is for XML input. You're not going to be able to put strictly CSV data in there. You could wrap your CSV in a root element and process the content of that instead of a variable, but why do all of this just to get it to work in that tool? – Daniel Haley Mar 27 '15 at 09:13
  • @DanielHaley Because the http://thescarms.com/dotnet/XSLT.aspx .net code is expecting a generic XSL template. It basically works similar to the xslttest.appspot.com. The .NET code accepts CSV data and uses the XSL template to give XML output – variable Mar 27 '15 at 09:15
  • @Tomalak - I didn't mean that CSV itself isn't complex and some processing would be hard to handle (ex. quotes aren't, but line breaks are). I meant if you have consistent input that is not super complex, there's no reason to rule out XSLT. Also, I think you're 100% correct about the OP and the decision to use XSLT. It could very well be that XSLT is not the right tool in this case. – Daniel Haley Mar 27 '15 at 09:18
  • 2
    @variable - It looks like their XSLT is looking for a root element named `ROOT` which is what I suggested; wrap your CSV in a root element. Updated example: http://xsltransform.net/eiZQaFu/1 – Daniel Haley Mar 27 '15 at 09:23
  • @variable - Are you also onkarsahas (the OP)? – Daniel Haley Mar 27 '15 at 09:29
  • No just found this interesting. – variable Mar 27 '15 at 09:31
  • Apologies to the OP for the spam. Hopefully this answer will help in some way. – Daniel Haley Mar 27 '15 at 09:33
  • @DanielHaley xsltransform.net/eiZQaFu/1 is what I was looking for. Thanks. – onkarsahas Mar 27 '15 at 09:42
  • HI @Daniel, can you suggest me the code in XSL version 1.0 – onkarsahas Mar 27 '15 at 10:11
  • @DanielHaley It seems your link to xsltransform.net is no longer available (dead link) do you have an alternate source for how to handle this sort of conversion without using a file? – JRSofty Apr 10 '19 at 12:09
  • 1
    @JRSofty - I think this is probably what was originally in the xsltransform.net link: http://xsltfiddle.liberty-development.net/pPzifpx – Daniel Haley Apr 10 '19 at 18:50
  • Is it possible to set linux path like /tmp/some_file.csv (java project) ? – Tomas Jan 15 '21 at 11:30
  • @DanielHaley I found your answer really helpful. But it partially works when some csv data columns contains "data , otherdata". It only pick the value as "data. Could you please suggest what changes required to read such values? Thanks, – Waqas Ali Razzaq Dec 29 '21 at 10:22