首页 > 解决方案 > XSLT - Nested XML to CSV output

问题描述

I am trying to convert a XML file to CSV using XSLT. I have the following (manually simplified version) of the XML file that I would like to convert. Each XML file can contain N number of records where N may vary.

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<report xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<reportheader>
<name>REPORT 1</name>
<version>v1</version>
</reportheader>
<recordtype>
    <account>
    <accountname>B</accountname>
    </account>
    <record>
    <time>12:00:00</time>
    <qty>10</qty>
    <price>20</price>
    </record>
    <record>
    <time>16:00:00</time>
    <qty>20</qty>
    <price>10</price>
    </record>   
</recordtype>
<recordtype>
    <account>
    <accountname>A</accountname>
    </account>
    <record>
    <time>16:00:00</time>
    <qty>5</qty>
    <price>10</price>
    </record>   
</recordtype>
</report>

The desired output would be the following:

name|version|accountname|time|qty|price
REPORT 1|v1|B|12:00:00|10|20
REPORT 1|v1|B|16:00:00|20|10
REPORT 1|v1|A|16:00:00|5|10

My current approach is to have two scripts. One XSLT that outputs all root nodes and the direct parent. In the second XSLT I then count the number of record nodes and loop through the csv column names N times. I then look for example to match /record/time[N]. The current approach borrows heavily from: https://pragmaticintegrator.wordpress.com/2012/10/28/transforming-xml-to-csv-via-xslt/ .

There are two problems with my current approach. 1, it assumes that every record always has every field available. 2. I cannot assign the right account to the records, because there might be only 6 accounts for 100 records. The way they are "loosely coupled" in the xml files makes my approach invalid.

Can someone help me find a more robust and efficient approach? Thanks in advance.

标签: xmlcsvxslt

解决方案


ancestor通过使用轴查找表亲值,您可以一次性完成

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="/">
    <xsl:text>name|version|accountname|time|qty|price&#xd;&#xa;</xsl:text>
    <xsl:apply-templates select="//record"/>
  </xsl:template>
  <xsl:template match="record">
    <xsl:value-of select="ancestor::report/reportheader/name"/>
    <xsl:text>|</xsl:text>
    <xsl:value-of select="ancestor::report/reportheader/version"/>
    <xsl:text>|</xsl:text>
    <xsl:value-of select="ancestor::recordtype/account/accountname"/>
    <xsl:text>|</xsl:text>
    <xsl:value-of select="time"/>
    <xsl:text>|</xsl:text>
    <xsl:value-of select="qty"/>
    <xsl:text>|</xsl:text>
    <xsl:value-of select="price"/>
    <xsl:text>&#xd;&#xa;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

推荐阅读