提问人:M-XPS 提问时间:9/24/2013 最后编辑:M-XPS 更新时间:11/16/2023 访问量:8080
如何使用 XSLT 1.0 将 Excel 转换为带有嵌套元素的 XML?
How to convert Excel to XML with nested elements using XSLT 1.0?
问:
我正在尝试将从 Excel 导出的 XML 结构转换为具有嵌套元素的新结构。 对我来说,如何用 XSLT v1.0 做到这一点有点太棘手了。
我的目标是将行数据放入结果 xml 的所需 lavel 中。 因此,如果一行仅包含 1 个带有数据的单元格,那么它就是一个类别,例如:
<projects>
<category name="001">
...
</category>
</projects>
如果一行有 2 个数据单元格,则它表示一个区域,并且应该是 previos 类别的子级。
<projects>
<category name="001">
<region name="AAA">...</region>
<region name="BBB">...</region>
</category>
</projects>
如果该行有 3 个或 4 个包含数据的单元格,则表示一个帐户,该帐户应是前一个区域的子级。
<projects>
<category name="001">
<region name="AAA">
<account name="lorem">...</account>
<account name="ipsum">...</account>
</region>
<region name="BBB">...</region>
...
</category>
</projects>
好的,到目前为止我所拥有的: 下面是来自 Excel 的 XML 文件。它具有具有重复行元素的简单结构。 每行包含 5 个单元格,但并非每个单元格也始终包含一个 Data 元素。 我试图用它来确定每行代表什么样的信息。
<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>
到目前为止,我已经创建了以下样式表,并且能够根据需要过滤所有信息。
<!-- Filter Excel Cells -->
<xsl:template match="ss:Row">
<xsl:if test="count(.//ss:Data) > 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>
我的结果现在看起来是这样的:
<?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>
不错(对我来说),但我需要以这种方式嵌套数据,而不是同一级别的每个数据。 我需要你的帮助!
<?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>
答:
我相信您需要对某些标签使用元素/属性调用,以将它们放入您需要的格式,如下所示
<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>
我还没有对此进行任何测试,但这应该为您指明所需的方向
将一维标记转换为层次结构是一个分组问题。在这种情况下,这是一个问题。group-starting-with
在 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>
输出:
<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>
请注意:您的输入缺少 和 prefix 的命名空间声明(即 )。此外,当使用起始模式分组时,匹配的元素是组的一部分(因此),并且可能存在第一个组,其中包含第一个匹配之前的所有元素(因此 )。元素的转换被省略了,以更好地理解主要问题。x
ss
xmlns:x="ns1" xmlns:ss="ns2"
current-group()[position()>1]
xsl:choose
Data
评论