Home » excel » XSLT Processing XML Converted from Excel

XSLT Processing XML Converted from Excel

Posted by: admin May 14, 2020 Leave a comment

Questions:

Here is the XML file converted from Excel Mac (Excel 2004 XML format):

The Excel file has four fields (Field 1 to 4) and each field has for data (Fieldn_Datam, n,m ranges from 1 to 4)

The XML output is like this:

<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:o="urn:schemas-microsoft-com:office:office"
        xmlns:x="urn:schemas-microsoft-com:office:excel"
        xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
        xmlns:html="http://www.w3.org/TR/REC-html40">
    <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
        <Author>Passiflora Cui</Author>
        <LastAuthor>Passiflora Cui</LastAuthor>
        <Created>2019-06-30T21:49:41Z</Created>
        <LastSaved>2019-06-30T21:50:54Z</LastSaved>
        <Version>16.00</Version>
    </DocumentProperties>
    <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
        <AllowPNG/>
    </OfficeDocumentSettings>
    <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
        <WindowHeight>16940</WindowHeight>
        <WindowWidth>27640</WindowWidth>
        <WindowTopX>5580</WindowTopX>
        <WindowTopY>3560</WindowTopY>
        <ProtectStructure>False</ProtectStructure>
        <ProtectWindows>False</ProtectWindows>
    </ExcelWorkbook>
    <Styles>
        <Style ss:ID="Default" ss:Name="Normal">
            <Alignment ss:Vertical="Bottom"/>
            <Borders/>
            <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
            <Interior/>
            <NumberFormat/>
            <Protection/>
        </Style>
        <Style ss:ID="s62">
            <Alignment ss:Horizontal="Center" ss:Vertical="Bottom"/>
        </Style>
    </Styles>
    <Worksheet ss:Name="Sheet1">
        <Table ss:ExpandedColumnCount="4" ss:ExpandedRowCount="5" x:FullColumns="1"
                x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="16">
            <Column ss:AutoFitWidth="0" ss:Width="74"/>
            <Row>
                <Cell>
                    <Data ss:Type="String">Field1</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field2</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field3</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field4</Data>
                </Cell>
            </Row>
            <Row>
                <Cell>
                    <Data ss:Type="String">Field1_Data1</Data>
                </Cell>
                <Cell ss:StyleID="s62">
                    <Data ss:Type="String">Field2_Data1</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field3_Data1</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field4_Data1</Data>
                </Cell>
            </Row>
            <Row>
                <Cell>
                    <Data ss:Type="String">Field1_Data2</Data>
                </Cell>
                <Cell ss:StyleID="s62">
                    <Data ss:Type="String">Field2_Data2</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field3_Data2</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field4_Data2</Data>
                </Cell>
            </Row>
            <Row>
                <Cell>
                    <Data ss:Type="String">Field1_Data3</Data>
                </Cell>
                <Cell ss:StyleID="s62">
                    <Data ss:Type="String">Field2_Data3</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field3_Data3</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field4_Data3</Data>
                </Cell>
            </Row>
            <Row>
                <Cell>
                    <Data ss:Type="String">Field1_Data4</Data>
                </Cell>
                <Cell ss:StyleID="s62">
                    <Data ss:Type="String">Field2_Data4</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field3_Data4</Data>
                </Cell>
                <Cell>
                    <Data ss:Type="String">Field4_Data4</Data>
                </Cell>
            </Row>
        </Table>
        <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
            <PageSetup>
                <Header x:Margin="0.3"/>
                <Footer x:Margin="0.3"/>
                <PageMargins x:Bottom="0.75" x:Left="0.7" x:Right="0.7" x:Top="0.75"/>
            </PageSetup>
            <Selected/>
            <Panes>
                <Pane>
                    <Number>3</Number>
                    <ActiveRow>5</ActiveRow>
                    <ActiveCol>5</ActiveCol>
                </Pane>
            </Panes>
            <ProtectObjects>False</ProtectObjects>
            <ProtectScenarios>False</ProtectScenarios>
        </WorksheetOptions>
    </Worksheet>
</Workbook>

And my XSLT file is like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method = "xml" indent = "yes"/>

    <xsl:template match = "Table">
        <Result>
            <xsl:for-each select="Row[position() > 1]">
                <Test>
                    <Field1><xsl:value-of select = "Cell[1]/Data"/></Field1>
                    <Field2><xsl:value-of select = "Cell[2]/Data"/></Field2>
                    <Field3><xsl:value-of select = "Cell[3]/Data"/></Field3>
                    <Field4><xsl:value-of select = "Cell[4]/Data"/></Field4>
                </Test>
            </xsl:for-each>
        </Result>
    </xsl:template>
</xsl:stylesheet>

And the desired result file looks like this:

<Result>
    <Test>
        <Field1>Field1_Data1</Field1>
        <Field2>Field2_Data1</Field2>
        <Field3>Field3_Data1</Field3>
        <Field4>Field4_Data1</Field4>
    </Test>
    <Test>
        <Field1>Field1_Data2</Field1>
        <Field2>Field2_Data2</Field2>
        <Field3>Field3_Data2</Field3>
        <Field4>Field4_Data2</Field4>
    </Test>
    <Test>
        <Field1>Field1_Data3</Field1>
        <Field2>Field2_Data3</Field2>
        <Field3>Field3_Data3</Field3>
        <Field4>Field4_Data3</Field4>
    </Test>
    <Test>
        <Field1>Field1_Data4</Field1>
        <Field2>Field2_Data4</Field2>
        <Field3>Field3_Data4</Field3>
        <Field4>Field4_Data4</Field4>
    </Test>
</Result>

However my XSLT does not work and it outputs all the strings as if the <xsl:template> has not being executed. I’ve tried a lot of XPaths such as //Table, Workbook/Worksheet/Table but none of them worked! Could anyone tell me where I’m wrong? Thanks in advance!

How to&Answers:

Your solution failed because you didn’t take the namespaces into account. Your XML root element <Workbook> set the default namespace xmlns="urn:schemas-microsoft-com:office:spreadsheet", hence itself and all of its children do have the same namespace. So you have to set this namespace in your XSLT as well.

Here, I used the prefix wb to designate the namespace

xmlns:wb="urn:schemas-microsoft-com:office:spreadsheet"

So the XSLT-1.0 file you desire could look like

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:wb="urn:schemas-microsoft-com:office:spreadsheet">
    <xsl:output method = "xml" indent = "yes"/>

    <!-- Ignore all free text() nodes -->
    <xsl:template match="text()" />

    <xsl:template match = "/wb:Workbook/wb:Worksheet/wb:Table">
        <xsl:element name="Result">
            <xsl:for-each select="wb:Row[position() > 1]">
                <Test>
                    <Field1><xsl:value-of select = "wb:Cell[1]/wb:Data"/></Field1>
                    <Field2><xsl:value-of select = "wb:Cell[2]/wb:Data"/></Field2>
                    <Field3><xsl:value-of select = "wb:Cell[3]/wb:Data"/></Field3>
                    <Field4><xsl:value-of select = "wb:Cell[4]/wb:Data"/></Field4>
                </Test>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Its result is as desired.
The prefix wb: of the newly defined namespace has been added to all XPath element references. That’s all you had to do to make your code work.