在每个循环中使用位置方法来访问节点索引/动态节点创建

时间:2013-10-07 18:09:58

标签: .net xml xslt

我正在尝试将保存为XML的Excel电子表格转换为实际的XML文档(删除所有对格式的引用并获取我的数据)。第一行是标题,应该是节点名称,其余行是数据。 XML将用于Windows窗体应用程序,转换将通过 XslTransform class of the XML namespace

我无法使用互操作来访问Excel,因为程序将使用的机器没有MS Office。如果任何列包含分隔符,则保存为CSV并读取文件将失败。我不想只写一个XML文件或硬编码信息,因为它需要在电子表格更改时进行更新。所以我认为动态生成XML是最好的选择。

为简单起见,我已从生成的XML文件中删除了大部分无关信息,以显示我感兴趣的工作表:

<?xml version="1.0"?>
 <Worksheet Name="AttributionImages">
  <Table ExpandedColumnCount="5" ExpandedRowCount="2" FullColumns="1"
   FullRows="1">
   <Column AutoFitWidth="0" Width="73.5"/>
   <Column AutoFitWidth="0" Width="84"/>
   <Column AutoFitWidth="0" Width="121.5"/>
   <Column AutoFitWidth="0" Width="146.25"/>
   <Column AutoFitWidth="0" Width="124.5"/>
   <Row>
    <Cell><Data Type="String">Name</Data></Cell>
    <Cell><Data Type="String">Source</Data></Cell>
    <Cell><Data Type="String">PicSet</Data></Cell>
    <Cell><Data Type="String">License</Data></Cell>
    <Cell><Data Type="String">Website</Data></Cell>
   </Row>
   <Row>
    <Cell><Data Type="String">Image1</Data></Cell>
    <Cell StyleID="s21" HRef="http://www.website.com/"><Data Type="String">Artist </Data></Cell>
    <Cell><Data Type="String">FreeIcons</Data></Cell>
    <Cell><Data Type="String">Creative Commons Attribution</Data></Cell>
    <Cell StyleID="s21" HRef="http://www.website.com/"><Data  
Type="String">http://www.website.com</Data></Cell>
   </Row>
  </Table>
 </Worksheet>

我的XSLT:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/Worksheet">
<xsl:element name="{@Name}">
    <xsl:for-each select="Table/Row">
        <xsl:if test="(position( )) &gt; 1">
                <Image>
                <xsl:for-each select="Cell">
                        <xsl:element name="{/Worksheet/Table/Row[1]/Cell[position()]/Data}">
                        <!-- <countNo><xsl:value-of select="position()"/></countNo> -->
                        <xsl:value-of select="Data"/>
                        </xsl:element>     
                </xsl:for-each>
                </Image>
        </xsl:if>

      </xsl:for-each>
</xsl:element>  
</xsl:template>
</xsl:stylesheet>

我赞成以下主题的答案,以帮助我开发上述XSLT:

预期输出:

  <?xml version="1.0" encoding="utf-8" ?> 
 <AttributionImages>
 <Image>
  <Name>Image1 </Name> 
  <Source>Artist</Source> 
  <PicSet>FreeIcons</PicSet> 
  <License>Creative Commons Attribution</License> 
  <Website>http://www.Website.com</Website> 
  </Image>
  </AttributionImages>

实际输出:

<?xml version="1.0" encoding="utf-8" ?> 
 <AttributionImages>
 <Image>
  <Name>Image1 </Name> 
  <Name>Artist</Name> 
  <Name>FreeIcons</Name> 
  <Name>Creative Commons Attribution</Name> 
  <Name>http://www.Website.com</Name> 
  </Image>
  </AttributionImages>

由于某种原因“/ Worksheet / Table / Row 1 / Cell [position()] / Data”始终等于第一行的第一个单元格(1 = name)。如果我用数字1-5替换position(),它将访问正确的索引。此外,我已注释掉的<countNo><xsl:value-of select="position()"/></countNo>将输出正确的循环计数。我需要为cell []索引值指定什么才能访问第一行中的所有节点?

1 个答案:

答案 0 :(得分:0)

这是因为当您执行/Worksheet/Table/Row1/Cell[position()]/Data时,对“position()”的调用将与xsl:for-each循环位于不同的上下文中,它将是选择“/ Worksheet / Table /”的上下文ROW1 /细胞“

您需要做的是先将位置存储在变量中,然后在xpath表达式中引用该位置:

<xsl:variable name="position" select="position()" />
<xsl:element name="{/Worksheet/Table/Row[1]/Cell[$position]/Data}">

实际上,使用密钥在这里查找标题可能稍微有点效率。也试试这个XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="header" match="Table/Row[1]/Cell" use="count(preceding-sibling::Cell) + 1"/>

   <xsl:template match="/Worksheet">
      <xsl:element name="{@Name}">
         <xsl:for-each select="Table/Row">
            <xsl:if test="(position( )) &gt; 1">
               <Image>
                  <xsl:for-each select="Cell">
                     <xsl:variable name="position" select="position()"/>
                     <xsl:element name="{key('header', $position)/Data/text()}">
                        <xsl:value-of select="Data"/>
                     </xsl:element>
                  </xsl:for-each>
               </Image>
            </xsl:if>
         </xsl:for-each>
      </xsl:element>
   </xsl:template>
</xsl:stylesheet>