C# 打开 XML : empty cells are getting skipped while getting data from EXCEL to DATATABLE

标签 c# datatable openxml openxml-sdk spreadsheetml

任务

excel 导入数据至 DataTable

问题

不包含任何数据的单元格将被跳过,并且行中具有数据的下一个单元格用作空列的值。 例如

A1 为空 A2 的值为 Tom然后在导入数据时A1获取 A2 的值并且 A2 保持为空

为了清楚起见,我在下面提供了一些屏幕截图

这是excel数据

enter image description here

这是从excel导入数据后的DataTable enter image description here

代码

public class ImportExcelOpenXml
{
    public static DataTable Fill_dataTable(string fileName)
    {
        DataTable dt = new DataTable();

        using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(fileName, false))
        {

            WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
            IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
            string relationshipId = sheets.First().Id.Value;
            WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
            Worksheet workSheet = worksheetPart.Worksheet;
            SheetData sheetData = workSheet.GetFirstChild<SheetData>();
            IEnumerable<Row> rows = sheetData.Descendants<Row>();

            foreach (Cell cell in rows.ElementAt(0))
            {
                dt.Columns.Add(GetCellValue(spreadSheetDocument, cell));
            }

            foreach (Row row in rows) //this will also include your header row...
            {
                DataRow tempRow = dt.NewRow();

                for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
                {
                    tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
                }

                dt.Rows.Add(tempRow);
            }

        }

        dt.Rows.RemoveAt(0); //...so i'm taking it out here.

        return dt;
    }


    public static string GetCellValue(SpreadsheetDocument document, Cell cell)
    {
        SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
        string value = cell.CellValue.InnerXml;

        if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
        {
            return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
        }
        else
        {
            return value;
        }
    }
}

我的想法

我觉得有问题

public IEnumerable<T> Descendants<T>() where T : OpenXmlElement;

如果我想使用 Descendants 计算列数

IEnumerable<Row> rows = sheetData.Descendants<<Row>();
int colCnt = rows.ElementAt(0).Count();

如果我使用 Descendants 获取行数

IEnumerable<Row> rows = sheetData.Descendants<<Row>();
int rowCnt = rows.Count();`

在这两种情况下 Descendants正在跳过空单元格

有没有Descendants的替代品? .

非常感谢您的建议

P.S:我也曾考虑过使用像 A1、A2 这样的列名来获取单元格值,但为了做到这一点,我必须获得列和行的确切数量,这不是可能通过使用 Descendants功能。

最佳答案

如果一行的所有单元格中都有一些数据,那么一切正常。但是,如果您碰巧连续有一个空单元格,那么事情就会变得一团糟。

为什么它首先发生

原因在于下面这行代码:

row.Descendants<Cell>().Count()

Count()函数为您提供行中非空单元格的数量,即它会在返回计数时忽略所有空单元格。所以,当你通过 row.Descendants<Cell>().ElementAt(i)作为 GetCellValue 的参数像这样的方法:

GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));

然后,它将查找下一个非空 单元格的内容,不一定是列索引i 处单元格的内容例如如果第一列为空,我们调用 ElementAt(1) ,它返回第二列中的值,我们的程序逻辑变得困惑。

解决方案:我们需要处理行中空单元格的出现,即我们需要计算出目标单元格的实际/有效列索引,以防它之前有一些空单元格在给定的行中。所以,你需要替换你的 for循环代码如下:

for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
      tempRow[i] = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i));
}

for (int i = 0; i < row.Descendants<Cell>().Count(); i++)
{
    Cell cell = row.Descendants<Cell>().ElementAt(i);
    int actualCellIndex = CellReferenceToIndex(cell);
    tempRow[actualCellIndex] = GetCellValue(spreadSheetDocument, cell);
}

此外,在您的代码中添加以下方法,该方法在上述修改后的代码段中用于获取任何单元格的实际/有效列索引:

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 行中的索引从 1 开始,这与从 0 开始的各种编程语言不同。

关于C# 打开 XML : empty cells are getting skipped while getting data from EXCEL to DATATABLE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36100011/

相关文章:

C# ExecuteScalar() - 必须小于无穷大?

C# 可选枚举类型属性 setter 在经典 .net 中编译但不在 .net 核心中编译? (cs0019)

c# - 检查DataTable中是否存在String/Record

guid - DOCX(Open XML WordprocessingDocument)中唯一 ID 的最佳位置

c# - 使用 LINQ Select 扩展方法后的项目顺序

c# - StreamReader,C#,偷看

c# - 使用 DataAdapter 填充表

javascript - 如何在执行内联编辑时显示包含数据表中的值的下拉列表?

c# - 将单元格的数据类型更改为 Double

c# - Word 模板中动态内容的自定义占位符/标签