为什么 OpenXML 读取行两次

Why does OpenXML read rows twice

我像这样计算两个工作表中的行数:

foreach (WorksheetPart worksheetPart in workbookPart.WorksheetParts)
{
    OpenXmlPartReader reader = new OpenXmlPartReader(worksheetPart);
    if (count == 0)
    {
        while (reader.Read())
        {
            if (reader.ElementType == typeof(Row))
            {
                count_first++;
            }
        }
    }
    else if (count == 1)
    {
        while (reader.Read())
        {
            if (reader.ElementType == typeof(Row))
            {
                count_second++;
            }
        }
    }
    count++;
}

对于 count_firstcount_second 中的工作表,我得到的数据行数是数据行数的两倍。为什么会这样,它究竟意味着什么?这是否意味着 OpenXML 将每个列表解析两次?

编辑

好吧,我找到了解决办法。为了马上得到它,我想,你应该把这个神圣的知识保存在某个秘密的地方。所以,这里是:

while (reader.Read())
{
    if (reader.ElementType == typeof(Row))
    {
        do
        {
            count_first++;
        }   while (reader.ReadNextSibling());
    }
}

这按预期工作:

while (reader.Read())
{
    if (reader.ElementType == typeof(Row))
    {
        do
        {
            count_first++;
        }   while (reader.ReadNextSibling());
    }
}

您得到两倍计数的原因是 OpenXmlReader 读取每个元素的方式。 reader 将打开和关闭节点视为独立的项目,可以通过检查 IsStartElement and IsEndElement 属性来区分它们。

为了演示这一点,您可以 运行 像这样:

using (OpenXmlReader reader = OpenXmlReader.Create(worksheetPart))
{
    while (reader.Read())
    {
        if (reader.ElementType == typeof(Row))
        {
            do
            {
                Console.WriteLine("{0} {1} {2}", 
                                  reader.ElementType,
                                  reader.IsStartElement,
                                  reader.IsEndElement);
            } while (reader.Read());

            Console.WriteLine("Finished");
        }
    }
}

对于具有两行两列的 sheet,这将产生如下所示的输出*(为了便于阅读,我突出显示了行):

Row True False
Cell True False
CellValue True False
CellValue False True
Cell False True
Cell True False
CellValue True False
CellValue False True
Cell False True
Row False True
Row True False
Cell True False
CellValue True False
CellValue False True
Cell False True
Cell True False
CellValue True False
CellValue False True
Cell False True
Row False True

有两种方法可以解决这个问题,具体取决于您阅读文档的方式。第一种方法(正如您在回答中指出的那样)是通过调用 ReadNextSibling 移动到下一个兄弟节点 - 这实际上是 "jumps" 结束元素(以及 Row 的任何子元素)。将上面的示例更改为在 do 循环中使用 ReadNextSibling

do
{
    Console.WriteLine("{0} {1} {2}", 
                       reader.ElementType,
                       reader.IsStartElement,
                       reader.IsEndElement);
} while (reader.ReadNextSibling());

您将获得以下输出*:

Row True False
Row True False

第二种方法是只计算开始元素(或者实际上是结束元素;只是不是两者):

while (reader.Read())
{
    if (reader.ElementType == typeof(Row) && reader.IsStartElement)
    {
        count_first++;
    }
}

您选择哪一个取决于您是否希望读取 Cell 值以及您希望如何读取它们(SAX 或 DOM ).

* 实际上,每一行都以 "DocumentFormat.OpenXml.Spreadsheet." 的命名空间为前缀,为了便于阅读,我已将其删除。