OpenXML - 单元格的日期类型为空。

12

我无法确定一个单元格是否为日期。

日期数据类型单元格

我注意到DataType为空,所以无法区分它是数字还是日期。

我正在使用下面的代码提取单元格:

WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(worksheetId);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
Row[] rows = worksheetPart.Worksheet.Descendants<Row>().ToArray();
for (int i = 0; i < rows.Length; i++)
{
    List<Cell> cells = rows[i].Elements<Cell>().ToList();
    foreach (var cell in cells) 
    {
        if (cell.DataType != null && cell.DataType.Value == CellValues.Date)
            //this line is not hit for some reason
         }
    }
}

我有什么遗漏吗?

3个回答

19

简而言之,它是null,因为它应该用于数字和日期类型。

有关msdn的OpenXML文档:

对于数字和日期类型,DataType属性的值为null。 对于字符串,它包含CellValues.SharedString的值,对于布尔值,则为CellValues.Boolean。

但是,可以通过 CellFormat 上的 NumberFormatId 区分日期和数字单元格格式。 诀窍在于找到哪个ID映射到什么格式。 您可以通过创建新的Excel文件并将单元格设置为问题格式(即日期)来找出要使用的格式:

enter image description here

然后使用7zip提取Excel文件并查看xl / styles.xml文件:

enter image description here

在上面的图像中,您可以看到此formatId 14将翻译成短日期。 完整格式列表请参见Office Open XML格式的ECMA-376文档(数字格式表应该在第4部分中被埋藏。他们将其移到了第1部分的18.8.30节中)。

我创建了一个枚举来表示最常见的formatIds:

private enum Formats
    {
        General = 0,
        Number = 1,
        Decimal = 2,
        Currency = 164,
        Accounting = 44,
        DateShort = 14,
        DateLong = 165,
        Time = 166,
        Percentage = 10,
        Fraction = 12,
        Scientific = 11,
        Text = 49
    }
您可以创建一个辅助函数,以您所需的方式获取格式化的值:
private static string GetFormattedCellValue(WorkbookPart workbookPart, Cell cell)
    {
        if (cell == null)
        {
            return null;
        }

        string value = "";
        if (cell.DataType == null) // number & dates
        {
            int styleIndex = (int)cell.StyleIndex.Value;
            CellFormat cellFormat = (CellFormat)workbookPart.WorkbookStylesPart.Stylesheet.CellFormats.ElementAt(styleIndex);
            uint formatId = cellFormat.NumberFormatId.Value;
            
            if (formatId == (uint)Formats.DateShort || formatId == (uint)Formats.DateLong)
            {
                double oaDate;
                if (double.TryParse(cell.InnerText, out oaDate))
                {
                    value = DateTime.FromOADate(oaDate).ToShortDateString();
                }
            }
            else
            {
                value = cell.InnerText;
            }
        }
        else // Shared string or boolean
        {
            switch (cell.DataType.Value)
            {
                case CellValues.SharedString:
                    SharedStringItem ssi = workbookPart.SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAt(int.Parse(cell.CellValue.InnerText));
                    value = ssi.Text.Text;
                    break;
                case CellValues.Boolean:
                    value = cell.CellValue.InnerText == "0" ? "false" : "true";
                    break;
                default:
                    value = cell.CellValue.InnerText;
                    break;
            }
        }

        return value;
    }

你知道在Open XML中是否也可以处理不同的编码类型,如果可以,我们该如何实现呢? - swcraft
关于 styleIndex = (int)cell.StyleIndex.Value; 这部分,StyleIndex 也可能为空,这会在尝试访问 Value 属性时导致错误。我先检查 StyleIndex 是否为空,如果是,则将 formatId 设为 0。 - Eric Barr

2
前面的回答说这些格式在第4部分有文档记录,但似乎它们已经移动到第1部分。我通过搜索#,##0.00找到了它们。它们目前在18.8.30号numFmt(数字格式)中,用于“文件中隐含而不是明确保存其formatCode值的数字格式”。请参见“18.8.31 numFmts(数字格式)”,其中包含此工作簿中的数字格式,由一系列numFmt记录组成,每个numFmt记录定义特定的数字格式,指示如何格式化和呈现单元格的数值。
以下是所有语言的隐含formatCode属性:
ID formatCode
0 General
1 0
2 0.00
3 #,##0
4 #,##0.00
9 0%
10 0.00%
11 0.00E+00
12 # ?/?
13 # ??/??
14 mm-dd-yy
15 d-mmm-yy
16 d-mmm
17 mmm-yy
18 h:mm AM/PM
19 h:mm:ss AM/PM
20 h:mm
21 h:mm:ss
22 m/d/yy h:mm
37 #,##0 ;(#,##0)
38 #,##0 ;[Red](#,##0)
39 #,##0.00;(#,##0.00)
40 #,##0.00;[Red](#,##0.00)
45 mm:ss
46 [h]:mm:ss
47 mmss.0
48 ##0.0E+0
49 @

还有许多其他的隐含格式代码是与语言相关的,例如:

28 [$-404]e"年"m"月"d"日" m"月"d"日"

0
你尝试过使用cell.HasValue吗?因为int和Datetime并不总是可空的,这取决于代码的编写方式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接