суббота, 24 апреля 2010 г.

Converting XML to CSV through XSL

В интернете достаточно примеров по преобразованию XML в CSV по средствам XSL, но все они заточены под американские стандарты, т.е. тупо значения, с запятой в качестве разделителя. Мне нужно было получить значения в двойных ковычках, с разделителем ";" и если в тексте встречается двойная ковычка, то она должна заменяться на две двойных ковычки. Подробности о CSV в википедии.

Исходный XML:

<?xml version="1.0" encoding="windows-1251"?>
<catalog>
       <cd>
               <title>Empire "Burlesque"</title>
               <artist>Bob Dylan</artist>
               <country>USA</country>
               <company>&quot;Columbia&quot;</company>
               <price>10.90</price>
               <year>1985</year>
       </cd>
       <cd>
               <title>Hide your heart</title>
               <artist>Bonnie Tyler</artist>
               <country>UK</country>
               <company>CBS Records</company>
               <price>9.90</price>
               <year>1988</year>
       </cd>
       <cd>
               <title>Greatest Hits</title>
               <artist>Dolly Parton</artist>
               <country>USA</country>
               <company>RCA</company>
               <price>9.90</price>
               <year>1982</year>
       </cd>
       <cd>
               <title>Still got the blues</title>
               <artist>Gary Moore</artist>
               <country>UK</country>
               <company>Virgin records</company>
               <price>10.20</price>
               <year>1990</year>
       </cd>
       <cd>
               <title>Eros</title>
               <artist>Eros Ramazzotti</artist>
               <country>EU</country>
               <company>BMG</company>
               <price>9.90</price>
               <year>1997</year>
       </cd>
</catalog>


XSL шаблон:

<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:variable name="qt">
  <xsl:text>"</xsl:text>
</xsl:variable>
<xsl:variable name="dqt">
  <xsl:text>""</xsl:text>
</xsl:variable>

<xsl:template match="/catalog">
  <xsl:apply-templates select="cd"/>
</xsl:template>

<xsl:template match="cd">
  <xsl:for-each select="*">
   <xsl:value-of select="$qt"/>
   <xsl:call-template name="replace-substring">
      <xsl:with-param name="original" select="."/>
      <xsl:with-param name="substring" select="$qt"/>
      <xsl:with-param name="replacement" select="$dqt"/>
   </xsl:call-template>
     <xsl:value-of select="$qt"/>
   <xsl:if test="position() != last()">
    <xsl:text>;</xsl:text>
   </xsl:if>
  </xsl:for-each>
  <xsl:text>&#13;&#10;</xsl:text>
</xsl:template>

 <xsl:template name="replace-substring">
    <xsl:param name="original" />
    <xsl:param name="substring" />
    <xsl:param name="replacement" />
    <xsl:choose>
      <xsl:when test="contains($original, $substring)">
        <xsl:value-of
          select="substring-before($original, $substring)" />
        <xsl:value-of select="$replacement" />
        <xsl:call-template name="replace-substring">
          <xsl:with-param name="original"
            select="substring-after($original, $substring)" />
          <xsl:with-param
            name="substring" select="$substring" />
          <xsl:with-param
            name="replacement" select="$replacement" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$original" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>


И результат:

"Empire ""Burlesque""";"Bob Dylan";"USA";"""Columbia""";"10.90";"1985"
"Hide your heart";"Bonnie Tyler";"UK";"CBS Records";"9.90";"1988"
"Greatest Hits";"Dolly Parton";"USA";"RCA";"9.90";"1982"
"Still got the blues";"Gary Moore";"UK";"Virgin records";"10.20";"1990"
"Eros";"Eros Ramazzotti";"EU";"BMG";"9.90";"1997"