开发者

Two phase transformation using XSLT 2.0

开发者 https://www.devze.com 2023-02-28 16:19 出处:网络
I am trying to take a CSV file as input and transform it into a XML.I\'m new to XSLT and I\'ve found a way to convert a CSV into XML (using an example from Andrew Welch) like so:

I am trying to take a CSV file as input and transform it into a XML. I'm new to XSLT and I've found a way to convert a CSV into XML (using an example from Andrew Welch) like so:

Input CSV file:

car manufacturer,model,color,price,inventory
subaru,outback,blue,23195,54
subaru,forester,silver,20495,23

And my output XML would be:

<?xml version="1.0" encoding="UTF-8"?>
<rows>
   <row>
      <column name="car manufacturer">subaru</column>
      <column name="model">outback</column>
      <column name="color">blue</column>
      <column name="price">23195</column>
      <column name="inventory">54</column>
   </row>
   <row>
      <column name="car manufacturer">subaru</column>
      <column name="model">forester</column>
      <column name="color">silver</column>
      <column name="price">20495</column>
      <column name="inventory">23</column>
   </row>
</rows>

My desired output is actually something similar to:

<stock>
   <model>
      <car>subaru outback</car>
      <color>blue</color>
      <price>23195</price>
      <inventory>54</inventory>
   </model>开发者_开发百科
   <model>
      <car>subaru forester</car>
      <color>silver</color>
      <price>20495</price>
      <inventory>23</inventory>
   </model>
</stock>

What I read is that it would best be done using a two phase transformation. The CSV to XML is done using XSLT 2.0, so I thought the two phase transformation would be done using that as well without using the node-set function.

So the first phase would be to take the original CSV file as input, and then output the intermediate XML shown above. Then take that intermediate XML, and pass it into another transformation to get the desired output.

Anyone can help on how the two phase transformation can be done? I'm having trouble passing the output of phase one as an input of phase 2?

I have something like this so far:

<xsl:import href="csv2xml.xsl"/>
<xsl:output method="xml" indent="yes" />

<xsl:variable name="intermediate">
    <xsl:apply-templates select="/" mode="csv2xml"/>
</xsl:variable>

<xsl:template match="rows" name="main">

 **[This is what I'm having trouble with]**

</xsl:template>


I don't see any reason why this transformation needs two phases - except perhaps to allow you to reuse existing code for one of the phases.

However, when you do need two phases, the general model is:

<xsl:template match="/">
  <xsl:variable name="phase-1-result">
    <xsl:apply-templates select="/" mode="phase-1"/>
  </xsl:variable>
  <xsl:apply-templates select="$phase-1-result" mode="phase-2"/>
</xsl:template>

with the template rules for phase 1 and phase 2 (and their apply-templates calls) all being in mode phase-1 or phase-2 respectively.


This XSLT 2.0 stylesheet:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:variable name="vLines"
         select="tokenize(unparsed-text('test.txt'),'(&#xD;)?&#xA;')"/>
        <xsl:variable name="vHeaders"
         select="tokenize($vLines[1],',')"/>
        <stock>
            <xsl:for-each select="$vLines[position()!=1]">
                <model>
                    <xsl:variable name="vColumns" select="tokenize(.,',')"/>
                    <xsl:for-each select="$vColumns">
                        <xsl:variable name="vPosition" select="position()"/>
                        <xsl:variable name="vHeader"
                         select="$vHeaders[$vPosition]"/>
                        <xsl:choose>
                            <xsl:when test="$vHeader = 'car manufacturer'">
                                <column name="car">
                                    <xsl:value-of
                                     select="(.,$vColumns[
                                                   index-of($vHeaders,'model')
                                                ])"/>
                                </column>
                            </xsl:when>
                            <xsl:when test="$vHeader = 'model'"/>
                            <xsl:otherwise>
                                <column name="{$vHeader}">
                                    <xsl:value-of select="."/>
                                </column>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each>
                </model>
            </xsl:for-each>
        </stock>
    </xsl:template>
</xsl:stylesheet>

Output:

<stock>
    <model>
        <column name="car">subaru outback</column>
        <column name="color">blue</column>
        <column name="price">23195</column>
        <column name="inventory">54</column>
    </model>
    <model>
        <column name="car">subaru forester</column>
        <column name="color">silver</column>
        <column name="price">20495</column>
        <column name="inventory">23</column>
    </model>
</stock>

Note: In XSLT 3.0 you will be able to apply templates to items in general.

EDIT: Correct names.


You can find here an example of how to do this with XSLT 3.0 :

http://www.stylusstudio.com/tutorials/intro-xslt-3.html

And see under "Text Manipulations".

0

精彩评论

暂无评论...
验证码 换一张
取 消