title: csv extraction from a multi-level xml
body: <p>I'm trying to extract csv from xml. The leaf element names from the entire xml form the header line and all the corresponding text values are the data rows. If a given leaf element is not present in a node, then print blank value. Below sample xml and output would explain what I'm trying to do.</p>
  
  <p>Input XML:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;itemList&gt;
      &lt;item&gt;
          &lt;userID&gt;123&lt;/userID&gt;
          &lt;userName&gt;ABC&lt;/userName&gt;
          &lt;orders SINGLE="Y"&gt;
              &lt;order&gt;
                  &lt;orderID&gt;0000377T&lt;/orderID&gt;
                  &lt;orderType&gt;online&lt;/orderType&gt;
              &lt;/order&gt;
          &lt;/orders&gt;
          &lt;details SINGLE="Y"&gt;
              &lt;detail&gt;
                  &lt;color&gt;black&lt;/color&gt;
                  &lt;make&gt;pluto&lt;/make&gt;
              &lt;/detail&gt;
          &lt;/details&gt;
          &lt;addresses SINGLE="N"&gt;
              &lt;address&gt;
                  &lt;addrID&gt;000111NR&lt;/addrID&gt;
                  &lt;addrName&gt;HOME&lt;/addrName&gt;
              &lt;/address&gt;
              &lt;address&gt;
                  &lt;addrID&gt;000111ST&lt;/addrID&gt;
                  &lt;addrName&gt;OFFICE&lt;/addrName&gt;
                  &lt;comment&gt;HQ&lt;/comment&gt;
              &lt;/address&gt;
          &lt;/addresses&gt;
      &lt;/item&gt;
      &lt;item&gt;
          &lt;userID&gt;456&lt;/userID&gt;
          &lt;userName&gt;DEF&lt;/userName&gt;
          &lt;orders SINGLE="Y"&gt;
              &lt;order&gt;
                  &lt;orderID&gt;0000377T&lt;/orderID&gt;
                  &lt;orderType&gt;phone&lt;/orderType&gt;
              &lt;/order&gt;
          &lt;/orders&gt;
          &lt;details SINGLE="Y"&gt;
              &lt;detail&gt;
                  &lt;color&gt;red&lt;/color&gt;
              &lt;/detail&gt;
          &lt;/details&gt;
          &lt;addresses SINGLE="N"&gt;
              &lt;address&gt;
                  &lt;addrID&gt;000222NR&lt;/addrID&gt;
                  &lt;addrName&gt;HOME&lt;/addrName&gt;
              &lt;/address&gt;
              &lt;address&gt;
                  &lt;delivery&gt;am&lt;/delivery&gt;
                  &lt;addrID&gt;000222ST&lt;/addrID&gt;
                  &lt;addrName&gt;OFFICE&lt;/addrName&gt;
              &lt;/address&gt;
          &lt;/addresses&gt;
      &lt;/item&gt;
  &lt;/itemList&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Expected output:</p>
  
  <pre><code>userID,userName,orderID,orderType,color,make,addrID,addrName,addrID,addrName,comment,delivery
  123,ABC,0000377T,online,black,pluto,000111NR,HOME,000111ST,OFFICE,HQ,
  456,DEF,0000377T,phone,red,,000222NR,HOME,000222ST,OFFICE,,am
  </code></pre>
  
  <h2>XSLT which I am able to frame so far:</h2>
  
  <pre><code>    &lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"&gt;
        &lt;xsl:output method="text" /&gt;
        &lt;xsl:strip-space elements="*" /&gt;
        &lt;xsl:variable name="newLine" select="'&amp;#xA;'" /&gt;
        &lt;xsl:variable name="delimiter" select="','" /&gt;
        &lt;xsl:key name="field" match="//*[not(*)]" use="local-name()" /&gt;
        &lt;xsl:variable name="allFields" select="//*[generate-id()=generate-id(key('field', local-name())[1])]" /&gt;
        &lt;xsl:template match="/"&gt;
  &lt;!-- print the header line --&gt;
          &lt;xsl:for-each select="$allFields"&gt;
            &lt;xsl:value-of select="local-name()" /&gt;
            &lt;xsl:if test="position() &amp;lt; last()"&gt;
              &lt;xsl:value-of select="$delimiter" /&gt;
            &lt;/xsl:if&gt;
          &lt;/xsl:for-each&gt;
          &lt;xsl:value-of select="$newLine" /&gt;
          &lt;xsl:apply-templates /&gt;
        &lt;/xsl:template&gt;
        &lt;xsl:template match="item"&gt;
          &lt;xsl:if test="position()!=1"&gt;
            &lt;xsl:value-of select="$newLine" /&gt;
          &lt;/xsl:if&gt;
          &lt;xsl:apply-templates select="descendant::*[not(*)]" mode="pass" /&gt;
        &lt;/xsl:template&gt;
        &lt;xsl:template match="*" mode="pass"&gt;
          &lt;xsl:if test="position()!=1"&gt;
            &lt;xsl:value-of select="$delimiter" /&gt;
          &lt;/xsl:if&gt;
          &lt;xsl:variable name="this" select="." /&gt;
          &lt;xsl:for-each select="$allFields"&gt;
            &lt;xsl:value-of select="$this[local-name() = local-name(current())]" /&gt;
          &lt;/xsl:for-each&gt;
        &lt;/xsl:template&gt;
      &lt;/xsl:stylesheet&gt;
  </code></pre>
  
  <h2>Output I'm getting with above XSLT when executed against above shown xml:</h2>
  
  <p>userID,userName,orderID,orderType,color,make,addrID,addrName,comment,delivery</p>
  
  <p>123,ABC,0000377T,online,black,pluto,000111NR,HOME,000111ST,OFFICE,HQ</p>
  
  <p>456,DEF,0000377T,phone,red,000222NR,HOME,am,000222ST,OFFICE</p>
  
  <p>The problem in this result is:
  1.no blank space is being print for a non-existent leaf element. 
  2.header line contains only one set of addrID, addrName whereas my input xml contains 2 sets.
  3.an empty line is printed in the output after every row even though I used strip-space at the start of XSLT.</p>
  
  <p>Can you please help to achieve the desired output as shown above under Expected output..? Thanks a lot in advance.</p>