打开从excel文件读取的xml

Posted

技术标签:

【中文标题】打开从excel文件读取的xml【英文标题】:open xml reading from excel file 【发布时间】:2014-05-30 21:17:39 【问题描述】:

我想在我的项目中实现 openXml sdk 2.5。我在这个link做所有事情

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO.Packaging;


static void Main(string[] args)
        

            String fileName = @"C:\OPENXML\BigData.xlsx";
            // Comment one of the following lines to test the method separately.
            ReadExcelFileDOM(fileName);    // DOM
            //ReadExcelFileSAX(fileName);    // SAX
        

        // The DOM approach.
        // Note that the code below works only for cells that contain numeric values.
        // 
        static void ReadExcelFileDOM(string fileName)
        
            using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
            
                WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
                WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
                SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
                string text;

                int rowCount= sheetData.Elements<Row>().Count();

                foreach (Row r in sheetData.Elements<Row>())
                
                    foreach (Cell c in r.Elements<Cell>())
                    
                        text = c.CellValue.Text;
                        Console.Write(text + " ");
                    
                
                Console.WriteLine();
                Console.ReadKey();
            
        

但我没有得到任何行。它没有进入循环。注意:我的电脑也设置了openXml sdk 2.5

我发现下面的代码适用于数值。对于字符串值它写 0 1 2 ...

 private static void Main(string[] args)
            
                var filePath = @"C:/OPENXML/BigData.xlsx";
                using (var document = SpreadsheetDocument.Open(filePath, false))
                
                    var workbookPart = document.WorkbookPart;
                    var workbook = workbookPart.Workbook;

                    var sheets = workbook.Descendants<Sheet>();
                    foreach (var sheet in sheets)
                    
                        var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
                        var sharedStringPart = workbookPart.SharedStringTablePart;
                        //var values = sharedStringPart.SharedStringTable.Elements<SharedStringItem>().ToArray();

                        string text;
                        var rows = worksheetPart.Worksheet.Descendants<Row>();
                        foreach (var row in rows)
                        
                            Console.WriteLine();
                            int count = row.Elements<Cell>().Count();

                            foreach (Cell c in row.Elements<Cell>())
                            

                                text = c.CellValue.InnerText;

                                Console.Write(text + " ");

                            
                        
                    
                

                Console.ReadLine();
            

【问题讨论】:

之所以写 0, 1, 2... 是因为 Excel 使用的共享字符串表。有关访问实际文本的基本方法,请参阅下面的答案。 【参考方案1】:

您的方法对我来说似乎没问题 - 因为它确实“进入了循环”。 不过,您也可以尝试以下方法:

void Main()

    string fileName = @"c:\path\to\my\file.xlsx";

    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    
        using (SpreadsheetDocument doc = SpreadsheetDocument.Open(fs, false))
        
            WorkbookPart workbookPart = doc.WorkbookPart;
            SharedStringTablePart sstpart = workbookPart.GetPartsOfType<SharedStringTablePart>().First();
            SharedStringTable sst = sstpart.SharedStringTable;

            WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
            Worksheet sheet = worksheetPart.Worksheet;

            var cells = sheet.Descendants<Cell>();
            var rows = sheet.Descendants<Row>();

            Console.WriteLine("Row count = 0", rows.LongCount());
            Console.WriteLine("Cell count = 0", cells.LongCount());

            // One way: go through each cell in the sheet
            foreach (Cell cell in cells)
            
                if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
                
                    int ssid = int.Parse(cell.CellValue.Text);
                    string str = sst.ChildElements[ssid].InnerText;
                    Console.WriteLine("Shared string 0: 1", ssid, str);
                
                else if (cell.CellValue != null)
                
                    Console.WriteLine("Cell contents: 0", cell.CellValue.Text);
                
             

             // Or... via each row
             foreach (Row row in rows)
             
                 foreach (Cell c in row.Elements<Cell>())
                 
                     if ((c.DataType != null) && (c.DataType ==           CellValues.SharedString))
                     
                         int ssid = int.Parse(c.CellValue.Text);
                         string str = sst.ChildElements[ssid].InnerText;
                         Console.WriteLine("Shared string 0: 1", ssid, str);
                     
                     else if (c.CellValue != null)
                     
                         Console.WriteLine("Cell contents: 0", c.CellValue.Text);
                     
                 
             
         
     
 

我使用文件流方法打开工作簿,因为这允许您以共享访问权限打开它 - 这样您就可以同时在 Excel 中打开工作簿。如果工作簿在其他地方打开,则 Spreadsheet.Open(... 方法将不起作用。

也许这就是你的代码不起作用的原因。

还要注意,在适当的地方使用 SharedStringTable 来获取单元格文本。

编辑 2018-07-11:

由于这篇文章仍在获得投票,我还应该指出,在许多情况下,使用ClosedXML 来操作/阅读/编辑您的工作簿可能要容易得多。文档示例非常用户友好,并且根据我有限的经验,编码更直接。请注意,它(尚未)实现所有 Excel 函数(例如 INDEX 和 MATCH),这可能是也可能不是问题。 [无论如何我都不想尝试在 OpenXML 中处理 INDEX 和 MATCH。]

【讨论】:

我认为你的代码绝对有效,但是当我将上面的代码复制到我的项目时它不起作用,我不知道为什么它不起作用。 那么你需要调试它或者提供比“它不起作用”更多的信息。例如,它在什么时候失败,是否产生异常,行数和单元格数是多少。此外,例如,一些 Assert 语句 - 例如 Assert(workbookPart != null);断言(工作表部分!= null);断言(表!= null);等等等等。 它创建关于 workbookPart 工作表部分的对象。但是当我想检查 ResultViews 我看到它 Empty = "Enumeration yielded no results" 我更改了 openxml 版本 v2.0.50727 和 Windowsbase v4.0.30319。版本有问题吗? 我在答案中提到了它 - 大多数在线文档都建议使用 SpreadsheetDocument.Open(... 方法,这很好用 - 但只有在工作簿没有在其他地方打开时才有效。为了什么我正在做我需要能够在 Excel 中打开工作簿并仍然使用 OpenXML 读取它。文件流方法允许我指定文件打开和共享模式。显然,如果尝试写入工作簿,但我想通过 OpenXML 进行只读访问。我怀疑它会更快,因为我认为 OpenXML 无论如何都会使用文件流。【参考方案2】:

我和 OP 有同样的问题,上面的答案对我不起作用。

我认为这是问题所在:当您在 Excel 中(不是以编程方式)创建文档时,默认情况下您有 3 张工作表,而具有 Sheet1 行数据的 WorksheetParts 是最后一个 WorksheetParts 元素,而不是第一个。

我通过在 Visual Studio 中监视 document.WorkbookPart.WorksheetParts、展开结果、然后查看所有子元素直到找到 HasChildren = true 的 SheetData 对象来解决这个问题。

试试这个:

// open the document read-only
SpreadSheetDocument document = SpreadsheetDocument.Open(filePath, false);
SharedStringTable sharedStringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;
string cellValue = null;

foreach (WorksheetPart worksheetPart in document.WorkbookPart.WorksheetParts)

    foreach (SheetData sheetData in worksheetPart.Worksheet.Elements<SheetData>())
    
        if (sheetData.HasChildren)
        
            foreach (Row row in sheetData.Elements<Row>())
            
                foreach (Cell cell in row.Elements<Cell>())
                
                    cellValue = cell.InnerText;

                    if (cell.DataType == CellValues.SharedString)
                    
                        Console.WriteLine("cell val: " + sharedStringTable.ElementAt(Int32.Parse(cellValue)).InnerText);
                    
                    else
                    
                        Console.WriteLine("cell val: " + cellValue);
                    
                
            
        
    

document.Close();

【讨论】:

谢谢,很好的例子!【参考方案3】:

阅读大型 Excel: openxml 有 DOMSAX 两种读取 excel 的方法。 DOM 消耗更多的 RAM 资源,因为它将整个 xml 内容(Excel 文件)加载到内存中,但它的强类型化方法。 另一方面,SAX 是事件库解析。 more here

因此,如果您面对大型 excel 文件,最好使用 SAX。

以下代码示例使用 SAX 方法,同时处理 excel 文件读取中的两个重要场景。

    open xml 会跳过空单元格,因此您的数据集面临位移和错误索引。 您还需要跳过空行

此函数返回当时单元格的确切实际索引并处理第一种情况。 from here

private static int CellReferenceToIndex(Cell cell)
        
            int index = 0;
            string reference = cell.CellReference.ToString().ToUpper();
            foreach (char ch in reference)
            
                if (Char.IsLetter(ch))
                
                    int value = (int)ch - (int)'A';
                    index = (index == 0) ? value : ((index + 1) * 26) + value;
                
                else
                    return index;
            
            return index;
        

读取excel sax方法的代码。

//i want to import excel to data table
            dt = new DataTable();

            using (SpreadsheetDocument document = SpreadsheetDocument.Open(path, false))
            

                WorkbookPart workbookPart = document.WorkbookPart;
                WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();

                OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);

                //row counter
                int rcnt = 0;

                while (reader.Read())
                


                    //find xml row element type 
                    //to understand the element type you can change your excel file eg : test.xlsx to test.zip
                    //and inside that you may observe the elements in xl/worksheets/sheet.xml
                    //that helps to understand openxml better
                    if (reader.ElementType == typeof(Row))
                    

                        //create data table row type to be populated by cells of this row
                        DataRow tempRow = dt.NewRow();



                        //***** HANDLE THE SECOND SENARIO*****
                        //if row has attribute means it is not a empty row
                        if (reader.HasAttributes)
                        
                            
                            //read the child of row element which is cells

                            //here first element
                            reader.ReadFirstChild();



                            do
                            
                                //find xml cell element type 
                                if (reader.ElementType == typeof(Cell))
                                
                                    Cell c = (Cell)reader.LoadCurrentElement();

                                    string cellValue;

                                    
                                    int actualCellIndex = CellReferenceToIndex(c);

                                    if (c.DataType != null && c.DataType == CellValues.SharedString)
                                    
                                        SharedStringItem ssi = workbookPart.SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAt(int.Parse(c.CellValue.InnerText));

                                        cellValue = ssi.Text.Text;
                                    
                                    else
                                    
                                        cellValue = c.CellValue.InnerText;
                                    



                                    //if row index is 0 its header so columns headers are added & also can do some headers check incase
                                    if (rcnt == 0)
                                    
                                        dt.Columns.Add(cellValue);
                                    
                                    else
                                    
                                        // instead of tempRow[c.CellReference] = cellValue;
                                        tempRow[actualCellIndex] = cellValue;
                                    

                                    

                                


                            
                            while (reader.ReadNextSibling());


                            //if its not the header row so append rowdata to the datatable
                            if (rcnt != 0)
                            
                                dt.Rows.Add(tempRow);
                            

                            rcnt++;


                        


                    





                


            

【讨论】:

以上是关于打开从excel文件读取的xml的主要内容,如果未能解决你的问题,请参考以下文章

如何读取xml文件导出为excel文件

EXCEL 数据透视表 不可读取

js读取本地excel文件出现问题,这是咋回事

php 下载excel文件不能打开

Excel2007中发现不可读取的内容

delphi 读取xml