为什么OpenXML读取行两次

时间:2015-03-06 13:52:59

标签: c# excel openxml

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

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());
    }
}

2 个答案:

答案 0 :(得分:3)

您获得两倍计数的原因是OpenXmlReader读取每个元素的方式。阅读器将打开和关闭节点视为可以通过检查IsStartElementIsEndElement属性来区分的独立项目。

为了证明这一点,您可以运行以下内容:

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");
        }
    }
}

对于包含两行和两列的工作表,我们将沿着以下*的行生成输出(我已经突出显示了行的可读性):

  

行真假
  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来移动到下一个兄弟 - 这实质上是“跳跃”结束元素(以及Row的任何子元素)。更改上面的示例以在ReadNextSibling循环中使用do

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”的命名空间。为了便于阅读,我删除了它。

答案 1 :(得分:0)

这可以按预期工作:

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