CSV to XML using xslt - how to have incrementing column name

前端 未结 4 1357
小鲜肉
小鲜肉 2021-01-03 13:42

I have this xslt to convert a csv to xml, works fine, except the tag is the same for all columns. I need it to increment like this


  

        
相关标签:
4条回答
  • 2021-01-03 13:55

    It looks as though csvtoxml is being called with a large string and it recursively works it's way through that string. position() won't work in this case because you're not working with a set of nodes.

    Instead you might be able to achieve what you're after with a counting param:

    <xsl:template name="csvtoxml">
        <!-- import $StringToTransform-->
        <xsl:param name="StringToTransform" select="''"/>
        <xsl:param name="ColumnNum" select="1"/>
        <xsl:choose>
            <!-- string contains linefeed-->
            <xsl:when test="contains($StringToTransform,',')">
                <!-- Get everything up to the first carriage return-->
                <xsl:element name="{concat('column', $ColumnNum)}">
                    <xsl:value-of select="substring-before($StringToTransform,',')"/>
                </xsl:element>
                <!-- repeat for the remainder of the original string-->
                <xsl:call-template name="csvtoxml">
                    <xsl:with-param name="StringToTransform" select="substring-after($StringToTransform,',')" />
                    <xsl:with-param name="ColumnNum" select="$ColumnNum + 1" />
                </xsl:call-template>
            </xsl:when>
            <!-- string does not contain newline, so just output it-->
            <xsl:otherwise>
                <xsl:element name="{concat('column', $ColumnNum)}">
                    <xsl:value-of select="$StringToTransform" />
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
    0 讨论(0)
  • 2021-01-03 14:01

    Although the OP is probably looking for an XSLT 1.0 solution, for interest here is an XSLT 2.0 solution. This solution requires you to download my csv-to-xml library style-sheet from here, which is discussed in this blog entry.

    With this as the input document, referred to in the style-sheet with uri 'gangt.csv' (use a parameter or adapt as you require)...

    gangt.csv

    3779490,916705,CS,60,34.89,Sauce/Cholula
    5918104,918958,CS,6,20.63,Pasta/Fresh/Cavatelli/6#/Frozen
    5064774,920723,CS,10,45.5,Cheese/Oaxaca
    3422752,925230,EA,8,69.6,Chipotle/Powder/Ground
    5955640,BB171,CS,30,50.7,Butter/Unsalted
    5295326,BC110005,CS,6000,54.95,Oil/Olive/Finishing
    

    Style-sheet

    ...this XSLT 2.0 style-sheet...

    <xsl:stylesheet version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:clib="http://www.seanbdurkin.id.au/xslt/csv-to-xml.xslt" 
      xmlns:xcsv="http://www.seanbdurkin.id.au/xslt/xcsv.xsd"
      exclude-result-prefixes="xsl clib xcsv">
    <xsl:import href="csv-to-xml.xslt" />
    <xsl:output indent="yes" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*" />
    
    <xsl:template match="/" >
     <data-set>
       <xsl:apply-templates select="clib:csv-to-xml('gangt.csv')/xcsv:row" />
     </data-set>
    </xsl:template>
    
    <xsl:template match="xcsv:row">
     <row>
       <xsl:apply-templates select="xcsv:value" />
     </row>
    </xsl:template>
    
    <xsl:template match="xcsv:value">
     <xsl:element name="column{position()}">
       <xsl:value-of select="." />
     </xsl:element>
    </xsl:template>
    
    </xsl:stylesheet>      
    

    Output

    ...produces this output document...

    <data-set>
       <row>
          <column1>3779490</column1>
          <column2>916705</column2>
          <column3>CS</column3>
          <column4>60</column4>
          <column5>34.89</column5>
          <column6>Sauce/Cholula</column6>
       </row>
       <row>
          <column1>5918104</column1>
          <column2>918958</column2>
          <column3>CS</column3>
          <column4>6</column4>
          <column5>20.63</column5>
          <column6>Pasta/Fresh/Cavatelli/6#/Frozen</column6>
       </row>
       <row>
          <column1>5064774</column1>
          <column2>920723</column2>
          <column3>CS</column3>
          <column4>10</column4>
          <column5>45.5</column5>
          <column6>Cheese/Oaxaca</column6>
       </row>
       <row>
          <column1>3422752</column1>
          <column2>925230</column2>
          <column3>EA</column3>
          <column4>8</column4>
          <column5>69.6</column5>
          <column6>Chipotle/Powder/Ground</column6>
       </row>
       <row>
          <column1>5955640</column1>
          <column2>BB171</column2>
          <column3>CS</column3>
          <column4>30</column4>
          <column5>50.7</column5>
          <column6>Butter/Unsalted</column6>
       </row>
       <row>
          <column1>5295326</column1>
          <column2>BC110005</column2>
          <column3>CS</column3>
          <column4>6000</column4>
          <column5>54.95</column5>
          <column6>Oil/Olive/Finishing</column6>
       </row>
    </data-set>
    
    0 讨论(0)
  • 2021-01-03 14:03

    I'm not sure why you're opting for XSLT in this case. It's not the first choice for string handling operations such as this, especially since your source data isn't XML, you're just wrapping it in a node to make it XML. A more obvious approach would be to handle the CSV via something like PHP.

    In any case, to answer the question, if you have access to EXSLT (this is often available with XSLT processors, e.g. PHP's):

    Runnable demo here (see output source).

    <!-- break into rows -->
    <xsl:variable name='rows' select='str:split(root, "&#xA;")' />
    
    <!-- root - kick things off -->
    <xsl:template match='/'>
        <root>
            <xsl:apply-templates select='$rows' mode='row' />
        </root>
    </xsl:template>
    
    <!-- rows -->
    <xsl:template match='token' mode='row'>
        <xsl:variable name='cols' select='str:split(., ",")' />
        <row>
            <xsl:apply-templates select='$cols' mode='col' />
        </row>
    </xsl:template>
    
    <!-- columns -->
    <xsl:template match='token' mode='col'>
        <xsl:element name='col{position()}'>
            <xsl:value-of select='.' />
        </xsl:element>
    </xsl:template>
    
    0 讨论(0)
  • 2021-01-03 14:10

    Although XSLT is capable of processing non-XML content, it seems to not be intended as a general-purpose text transformation tool. As a result, most of the tools available to you are about manipulating XML infoset constructs like elements and attributes. There is a little support for text strings, but not much. So position() is defined in terms of input nodes.

    http://www.w3.org/TR/xpath/#section-Node-Set-Functions:

    The position function returns a number equal to the context position from the expression evaluation context.

    http://www.w3.org/TR/xslt#section-Expressions:

    the context position comes from the position of the current node in the current node list; the first position is 1

    Since your input is just a text string, you are always in position 1. I can think of only one way to do this with XSLT. Transform twice. The first transform gives you the basic structure with un-numbered column elements. The second transform numbers the column elements. Because you are selecting nodes from an XML document the second time, position() should have the values you want.

    0 讨论(0)
提交回复
热议问题