简体   繁体   English

如何使用 XSLT 1.0 将 Excel 转换为带有嵌套元素的 XML?

[英]How to convert Excel to XML with nested elements using XSLT 1.0?

I'm trying to convert an XML structure exported from Excel in a new structure with nested elements.我正在尝试将从 Excel 导出的 XML 结构转换为具有嵌套元素的新结构。 It's a bit too tricky for me to figure out, how to do so with XSLT v1.0.我很难弄清楚如何使用 XSLT v1.0 做到这一点。

My goal is to put the row data into the desired lavel of the result xml.我的目标是将行数据放入结果 xml 的所需级别。 So if a row contains only 1 cell with data, then it is a category for example:因此,如果一行仅包含 1 个包含数据的单元格,则它是一个类别,例如:

<projects> 
<category name="001">
...
</category>
</projects>

If a row has 2 data cells it represents a regions and should be a child of the previos category.如果一行有 2 个数据单元格,则它代表一个区域,应该是 previos 类别的子级。

<projects> 
<category name="001">
<region name="AAA">...</region>
<region name="BBB">...</region>
</category>
</projects>

If the row has 3 or 4 cells with data its represents an account, which should be a child of the previous region.如果该行有 3 或 4 个包含数据的单元格,则它表示一个帐户,该帐户应该是前一个区域的子级。

<projects> 
<category name="001">
<region name="AAA">
<account name="lorem">...</account>
<account name="ipsum">...</account>
</region>
<region name="BBB">...</region>
...
</category>
</projects>

Ok, what I have so far: Here's the XML file from Excel.好的,到目前为止我所拥有的:这是来自 Excel 的 XML 文件。 It has a simple structure with repeating row elements.它具有简单的结构,具有重复的行元素。 Each row contains 5 cells, but not every cell always contains a Data element too.每行包含 5 个单元格,但并非每个单元格都始终包含一个 Data 元素。 I tried to use this to determine what kind of information each row represents.我试图用它来确定每行代表什么样的信息。

    <Table ss:ExpandedColumnCount="6" ss:ExpandedRowCount="69" x:FullColumns="1"    x:FullRows="1" ss:StyleID="s62" ss:DefaultColumnWidth="69">
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"/>
    <Cell ss:StyleID="s69"/>
    <Cell ss:StyleID="s68"/>
    <Cell ss:StyleID="s68"/>
    <Cell ss:StyleID="s68"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="15">
    <Cell ss:Index="2" ss:StyleID="s70"><Data ss:Type="String">Categorie 004</Data></Cell>
    <Cell ss:StyleID="s71"/>
    <Cell ss:StyleID="s70"/>
    <Cell ss:StyleID="s72"/>
    <Cell ss:StyleID="s70"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">AAA</Data></Cell>
    <Cell ss:StyleID="s69"/>
    <Cell ss:StyleID="s69"><Data ss:Type="String">X</Data></Cell>
    <Cell ss:StyleID="s73"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.01</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"/>
    <Cell ss:StyleID="s69"/>
    <Cell ss:StyleID="s68"/>
    <Cell ss:StyleID="s73"/>
    <Cell ss:StyleID="s68"/>
   </Row>
   <Row ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"/>
    <Cell ss:StyleID="s69"/>
    <Cell ss:StyleID="s68"/>
    <Cell ss:StyleID="s73"/>
    <Cell ss:StyleID="s68"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"/>
    <Cell ss:StyleID="s69"/>
    <Cell ss:StyleID="s68"/>
    <Cell ss:StyleID="s73"/>
    <Cell ss:StyleID="s68"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="15">
    <Cell ss:Index="2" ss:StyleID="s70"><Data ss:Type="String">Categorie 001</Data></Cell>
    <Cell ss:StyleID="s71"/>
    <Cell ss:StyleID="s70"/>
    <Cell ss:StyleID="s72"/>
    <Cell ss:StyleID="s70"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s82"><Data ss:Type="String">AAA</Data></Cell>
    <Cell ss:StyleID="s83"/>
    <Cell ss:StyleID="s82"/>
    <Cell ss:StyleID="s84"/>
    <Cell ss:StyleID="s85"><Data ss:Type="Number">3.0800000000000001E-2</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">dolor</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">123</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">A</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.01</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="15">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">sit amet</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">445</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">B</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.03</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">consetetur</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">36</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">B</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">8.0000000000000004E-4</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s82"><Data ss:Type="String">BBB</Data></Cell>
    <Cell ss:StyleID="s83"/>
    <Cell ss:StyleID="s82"/>
    <Cell ss:StyleID="s84"/>
    <Cell ss:StyleID="s85"><Data ss:Type="Number">0.03</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">sadipscing</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">666</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">A</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.01</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="15">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">elitr</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">97</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">C</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.02</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="15">
    <Cell ss:Index="2" ss:StyleID="s68"/>
    <Cell ss:StyleID="s69"/>
    <Cell ss:StyleID="s68"/>
    <Cell ss:StyleID="s73"/>
    <Cell ss:StyleID="s68"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s70"><Data ss:Type="String">Categorie 001</Data></Cell>
    <Cell ss:StyleID="s71"/>
    <Cell ss:StyleID="s70"/>
    <Cell ss:StyleID="s72"/>
    <Cell ss:StyleID="s70"/>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s82"><Data ss:Type="String">AAA</Data></Cell>
    <Cell ss:StyleID="s83"/>
    <Cell ss:StyleID="s82"/>
    <Cell ss:StyleID="s84"/>
    <Cell ss:StyleID="s85"><Data ss:Type="Number">0.04</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">aliquyam</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">65</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">A</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.02</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">ipsum</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">99</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">B</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.02</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s82"><Data ss:Type="String">BBB</Data></Cell>
    <Cell ss:StyleID="s83"/>
    <Cell ss:StyleID="s82"/>
    <Cell ss:StyleID="s84"/>
    <Cell ss:StyleID="s85"><Data ss:Type="Number">0.01</Data></Cell>
   </Row>
   <Row ss:AutoFitHeight="0" ss:Height="14.25">
    <Cell ss:Index="2" ss:StyleID="s68"><Data ss:Type="String">lorem</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="Number">321</Data></Cell>
    <Cell ss:StyleID="s69"><Data ss:Type="String">C</Data></Cell>
    <Cell ss:StyleID="s86"/>
    <Cell ss:StyleID="s74"><Data ss:Type="Number">0.01</Data></Cell>
   </Row>
  </Table>

So far I've created the following stylesheet and where able to filter all information as needed.到目前为止,我已经创建了以下样式表,并且能够根据需要过滤所有信息。

<!-- Filter Excel Cells -->
<xsl:template match="ss:Row">
    <xsl:if test="count(.//ss:Data) &gt; 0">
        <xsl:choose>
            <xsl:when test="count(.//ss:Data)=1">
                <Categorie>
                    <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
                </Categorie>
            </xsl:when>
            <xsl:when test="count(.//ss:Data)=2">
                <Region>
                    <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
                </Region>
            </xsl:when>
            <xsl:when test="count(.//ss:Data)=3">
                <xsl:choose>
                    <xsl:when test=".//ss:Cell[5]/ss:Data='Rating'">
                        <Header>
                            <Text1>
                                <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
                            </Text1>
                            <Text2>
                                <xsl:value-of select=".//ss:Cell[3]/ss:Data"/>
                            </Text2>
                            <Text3>
                                <xsl:value-of select=".//ss:Cell[5]/ss:Data"/>
                            </Text3>
                        </Header>
                    </xsl:when>
                    <xsl:otherwise>
                        <Account>
                            <Name>
                                <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
                            </Name>
                            <Value>
                                <xsl:value-of select=".//ss:Cell[3]/ss:Data"/>
                            </Value>
                            <Rating>
                                <xsl:value-of select=".//ss:Cell[5]/ss:Data"/>
                            </Rating>
                        </Account>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:when test="count(.//ss:Data)=4">
                <xsl:choose>
                    <xsl:when test=".//ss:Cell[5]/ss:Data='Gewicht'">
                        <Header>
                            <Text1>
                                <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
                            </Text1>
                            <Text2>
                                <xsl:value-of select=".//ss:Cell[2]/ss:Data"/>
                            </Text2>
                            <Text3>
                                <xsl:value-of select=".//ss:Cell[3]/ss:Data"/>
                            </Text3>
                            <Text4>
                                <xsl:value-of select=".//ss:Cell[5]/ss:Data"/>
                            </Text4>
                        </Header>
                    </xsl:when>
                    <xsl:otherwise>
                        <Account>
                            <Name>
                                <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
                            </Name>
                            <Type>
                                <xsl:value-of select=".//ss:Cell[2]/ss:Data"/>
                            </Type>
                            <Value>
                                <xsl:value-of select=".//ss:Cell[3]/ss:Data"/>
                            </Value>
                            <Rating>
                                <xsl:value-of select=".//ss:Cell[5]/ss:Data"/>
                            </Rating>
                        </Account>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
        </xsl:choose>
    </xsl:if>
</xsl:template>

My results now looks this way:我的结果现在看起来是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<Projects xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
    <Categorie>Categorie 004</Categorie>
    <Region>AAA</Region>
    <Account>
        <Name>AAA</Name>
        <Value>X</Value>
        <Rating>0.01</Rating>
    </Account>
    <Account>
        <Name>Name</Name>
        <Type>Value</Type>
        <Value>Type</Value>
        <Rating>Rating</Rating>
    </Account>
    <Categorie>Categorie 001</Categorie>
    <Region>AAA</Region>
    <Account>
        <Name>dolor</Name>
        <Type>123</Type>
        <Value>A</Value>
        <Rating>0.01</Rating>
    </Account>
    <Account>
        <Name>sit amet</Name>
        <Type>445</Type>
        <Value>B</Value>
        <Rating>0.03</Rating>
    </Account>
    <Account>
        <Name>consetetur</Name>
        <Type>36</Type>
        <Value>B</Value>
        <Rating>8.0000000000000004E-4</Rating>
    </Account>
    <Region>BBB</Region>
    <Account>
        <Name>sadipscing</Name>
        <Type>666</Type>
        <Value>A</Value>
        <Rating>0.01</Rating>
    </Account>
    <Account>
        <Name>elitr</Name>
        <Type>97</Type>
        <Value>C</Value>
        <Rating>0.02</Rating>
    </Account>
    <Account>
        <Name>Name</Name>
        <Type>Value</Type>
        <Value>Type</Value>
        <Rating>Rating</Rating>
    </Account>
    <Categorie>Categorie 001</Categorie>
    <Region>AAA</Region>
    <Account>
        <Name>aliquyam</Name>
        <Type>65</Type>
        <Value>A</Value>
        <Rating>0.02</Rating>
    </Account>
    <Account>
        <Name>ipsum</Name>
        <Type>99</Type>
        <Value>B</Value>
        <Rating>0.02</Rating>
    </Account>
    <Region>BBB</Region>
    <Account>
        <Name>lorem</Name>
        <Type>321</Type>
        <Value>C</Value>
        <Rating>0.01</Rating>
    </Account>
</Projects>

Not bad (for me), but I need the data to be nested like this way and not every data in the same level.不错(对我来说),但我需要像这样嵌套数据,而不是每个数据都在同一级别。 I need your help!我需要你的帮助!

<?xml version="1.0" encoding="UTF-8"?>
<Projects xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
    <Categorie name="001"/>
        <Region name="AAA">
            <Account>
                <Name>Name</Name>
                <Type>Value</Type>
                <Value>Type</Value>
                <Rating>Rating</Rating>
            </Account>
            <Account>
                <Name>Name</Name>
                <Type>Value</Type>
                <Value>Type</Value>
                <Rating>Rating</Rating>
            </Account>
        </Region>
        <Region name="BBB">
            <Account>
                <Name>Name</Name>
                <Type>Value</Type>
                <Value>Type</Value>
                <Rating>Rating</Rating>
            </Account>
            <Account>
                <Name>Name</Name>
                <Type>Value</Type>
                <Value>Type</Value>
                <Rating>Rating</Rating>
            </Account>
            ...
        </Region>
        <Categorie name="002"/>
            <Region name="AAA">
                ...
            </Region>
        </Categorie>
</Projects>

i believe you need to use element/attribute calls on some of the tags to put them in the format you require such as the below我相信您需要在某些标签上使用元素/属性调用,以将它们置于您需要的格式中,如下所示

<xsl:element name="Categorie">
        <xsl:attribute name="name">
                 <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
        </xsl:attribute>
        <xsl:element name="Region">
            <xsl:attribute name="name">
                 <xsl:value-of select=".//ss:Cell[1]/ss:Data"/>
            </xsl:attribute>
          //rest of transform here
        </xsl:element>      
 </xsl:element>

I have not ran any sort of test on this yet, but this should point you in the direction you need我还没有对此进行任何类型的测试,但这应该为您指明所需的方向

Transforming one dimension markup to hierarchy is a grouping problem.将一维标记转换为层次结构是一个分组问题。 In this case a group-starting-with problem.在这种情况下,一个group-starting-with问题。

In XSLT 2.0在 XSLT 2.0 中

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:template match="Table">
        <xsl:for-each-group 
            select="Row[.//Data]" 
            group-starting-with="Row[count(.//Data)=1]">
            <Categorie>
                <xsl:for-each-group 
                    select="current-group()[position()>1]" 
                    group-starting-with="Row[count(.//Data)=2]">
                    <xsl:choose>
                        <xsl:when test="not(self::Row[count(.//Data)=2])">
                            <xsl:apply-templates select="current-group()"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <Region>
                                <xsl:apply-templates 
                                    select="current-group()[position()>1]"/>
                            </Region>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </Categorie>
        </xsl:for-each-group>
    </xsl:template>

   <xsl:template match="Row">
        <Account>
            <xsl:apply-templates/>
        </Account>
    </xsl:template>

    <xsl:template match="Row[.//Cell[5]/Data[.='Rating' or .='Gewicht']]">
        <Header>
            <xsl:apply-templates/>
        </Header>
    </xsl:template>

    <xsl:template match="Data">
        <Data>
            <xsl:value-of select="."/>
        </Data>
    </xsl:template>    
</xsl:stylesheet>

Outputs:输出:

<Categorie>
   <Account>
      <Data>AAA</Data>
      <Data>X</Data>
      <Data>0.01</Data>
   </Account>
</Categorie>
<Categorie>
   <Region>
      <Account>
         <Data>dolor</Data>
         <Data>123</Data>
         <Data>A</Data>
         <Data>0.01</Data>
      </Account>
      <Account>
         <Data>sit amet</Data>
         <Data>445</Data>
         <Data>B</Data>
         <Data>0.03</Data>
      </Account>
      <Account>
         <Data>consetetur</Data>
         <Data>36</Data>
         <Data>B</Data>
         <Data>8.0000000000000004E-4</Data>
      </Account>
   </Region>
   <Region>
      <Account>
         <Data>sadipscing</Data>
         <Data>666</Data>
         <Data>A</Data>
         <Data>0.01</Data>
      </Account>
      <Account>
         <Data>elitr</Data>
         <Data>97</Data>
         <Data>C</Data>
         <Data>0.02</Data>
      </Account>
   </Region>
</Categorie>
<Categorie>
   <Region>
      <Account>
         <Data>aliquyam</Data>
         <Data>65</Data>
         <Data>A</Data>
         <Data>0.02</Data>
      </Account>
      <Account>
         <Data>ipsum</Data>
         <Data>99</Data>
         <Data>B</Data>
         <Data>0.02</Data>
      </Account>
   </Region>
   <Region>
      <Account>
         <Data>lorem</Data>
         <Data>321</Data>
         <Data>C</Data>
         <Data>0.01</Data>
      </Account>
   </Region>
</Categorie>

Do note: your input is missing namespace declarations for x and ss prefix (ie xmlns:x="ns1" xmlns:ss="ns2" ).请注意:您的输入缺少xss前缀的命名空间声明(即xmlns:x="ns1" xmlns:ss="ns2" )。 Also, when grouping with starting pattern the matched element is part of the group (thus current-group()[position()>1] ) and there might be a first group with all the elements before the first match (thus the xsl:choose ).此外,当使用起始模式分组时,匹配的元素是组的一部分(因此current-group()[position()>1] )并且在第一次匹配之前可能存在包含所有元素的第一组(因此xsl:choose )。 The transformation of Data elements is left out to better understand the main problem.为了更好地理解主要问题,省略了Data元素的转换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM