C# 为什么OpenXML读取行两次

C# 为什么OpenXML读取行两次,c#,excel,openxml,C#,Excel,Openxml,我在两个工作表中计算行数,如下所示: foreach (WorksheetPart worksheetPart in workbookPart.WorksheetParts) { OpenXmlPartReader reader = new OpenXmlPartReader(worksheetPart); if (count == 0) { while (reader.Read()) { if (reader.El

我在两个工作表中计算行数,如下所示:

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++;
}
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");
        }
    }
}
对于
count\u first
count\u 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
读取每个元素的方式。读取器将打开和关闭节点视为独立项,可以通过检查和属性来区分这些独立项

要演示这一点,您可以运行如下操作:

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++;
}
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");
        }
    }
}
这将为具有两行两列的工作表生成以下*行的输出(为了便于阅读,我突出显示了这些行):

行真假
单元格真假
CellValue True False
CellValue False True
单元格假-真
单元格真假
CellValue True False
CellValue False True
单元格假-真
行假-真
行真假
单元格真假
CellValue True False
CellValue False True
单元格假-真
单元格真假
CellValue True False
CellValue False True
单元格假-真
行假真

根据您阅读文档的方式,有两种方法可以解决此问题。第一种方法(正如您在回答中指出的那样)是通过调用
ReadNextSibling
移动到下一个同级-这实质上是“跳转”结束元素(以及
行的任何子元素
)。将上述示例更改为在
do
循环中使用
ReadNextSibling

do
{
    Console.WriteLine("{0} {1} {2}", 
                       reader.ElementType,
                       reader.IsStartElement,
                       reader.IsEndElement);
} while (reader.ReadNextSibling());
您将获得以下内容的输出*:

行真假
行真假

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

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

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

如果您有答案,请将其作为答案发布,而不是对您的问题进行编辑。此外,在延迟一段时间后,您可以将其标记为已接受的答案。在这里,回答自己的问题是完全可以接受的行为。但我们知道,当问题得到公认的答案时,问题已经得到了令人满意的处理(目前,您的问题似乎仍未得到回答)