代码之家  ›  专栏  ›  技术社区  ›  Alex Angas Colin

使用readToDescendant和/或readElementContentAsObject更正XmlReader问题

  •  1
  • Alex Angas Colin  · 技术社区  · 14 年前

    我在一个通常非常好的开源项目中研究一个神秘的bug Excel Data Reader . 它跳过了从我的特定openxml.xlsx电子表格中读取的值。

    问题发生在 ReadSheetRow method (下面的演示代码)。源XML由Excel保存,不包含出现奇怪行为时的空白。但是,用空白重新格式化的XML(例如,在Visual Studio中,转到“编辑”、“高级”、“格式化文档”)工作完全正常!

    带空白的测试数据:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
        <sheetData>
            <row r="5" spans="1:73" s="7" customFormat="1">
                <c r="B5" s="12">
                    <v>39844</v>
                </c>
                <c r="C5" s="8"/>
                <c r="D5" s="8"/>
                <c r="E5" s="8"/>
                <c r="F5" s="8"/>
                <c r="G5" s="8"/>
                <c r="H5" s="12">
                    <v>39872</v>
                </c>
                <c r="I5" s="8"/>
                <c r="J5" s="8"/>
                <c r="K5" s="8"/>
                <c r="L5" s="8"/>
                <c r="M5" s="8"/>
                <c r="N5" s="12">
                    <v>39903</v>
                </c>
            </row>
        </sheetData>
    </worksheet>
    

    没有空白的测试数据:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheetData><row r="5" spans="1:73" s="7" customFormat="1"><c r="B5" s="12"><v>39844</v></c><c r="C5" s="8"/><c r="D5" s="8"/><c r="E5" s="8"/><c r="F5" s="8"/><c r="G5" s="8"/><c r="H5" s="12"><v>39872</v></c><c r="I5" s="8"/><c r="J5" s="8"/><c r="K5" s="8"/><c r="L5" s="8"/><c r="M5" s="8"/><c r="N5" s="12"><v>39903</v></c></row></sheetData></worksheet>
    

    演示问题的示例代码:

    注意 后输出 _xmlReader.Read() , B 之后 ReadToDescendant ,和 C 之后 ReadElementContentAsObject .

    while (reader.Read())
    {
        if (reader.NodeType != XmlNodeType.Whitespace) outStream.WriteLine(String.Format("*A* NodeType: {0}, Name: '{1}', Empty: {2}, Value: '{3}'", reader.NodeType, reader.Name, reader.IsEmptyElement, reader.Value));
    
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "c")
        {
            string a_s = reader.GetAttribute("s");
            string a_t = reader.GetAttribute("t");
            string a_r = reader.GetAttribute("r");
    
            bool matchingDescendantFound = reader.ReadToDescendant("v");
            if (reader.NodeType != XmlNodeType.Whitespace) outStream.WriteLine(String.Format("*B* NodeType: {0}, Name: '{1}', Empty: {2}, Value: '{3}'", reader.NodeType, reader.Name, reader.IsEmptyElement, reader.Value));
            object o = reader.ReadElementContentAsObject();
            if (reader.NodeType != XmlNodeType.Whitespace) outStream.WriteLine(String.Format("*C* NodeType: {0}, Name: '{1}', Empty: {2}, Value: '{3}'", reader.NodeType, reader.Name, reader.IsEmptyElement, reader.Value));
        }
    }
    

    带有空白的XML的测试结果:

    *A* NodeType: XmlDeclaration, Name: 'xml', Empty: False, Value: 'version="1.0" encoding="UTF-8" standalone="yes"'
    *A* NodeType: Element, Name: 'worksheet', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'sheetData', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'row', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'c', Empty: False, Value: ''
    *B* NodeType: Element, Name: 'v', Empty: False, Value: ''
    *A* NodeType: EndElement, Name: 'c', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'c', Empty: True, Value: ''
    *B* NodeType: Element, Name: 'c', Empty: True, Value: ''
    ...
    

    不带空白的XML测试结果:

    *A* NodeType: XmlDeclaration, Name: 'xml', Empty: False, Value: 'version="1.0" encoding="UTF-8" standalone="yes"'
    *A* NodeType: Element, Name: 'worksheet', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'sheetData', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'row', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'c', Empty: False, Value: ''
    *B* NodeType: Element, Name: 'v', Empty: False, Value: ''
    *C* NodeType: EndElement, Name: 'c', Empty: False, Value: ''
    *A* NodeType: Element, Name: 'c', Empty: True, Value: ''
    *B* NodeType: Element, Name: 'c', Empty: True, Value: ''
    ...
    

    模式更改表示 读取元素内容对象 或者可能是 可读性 将XmlReader移动到。

    有人知道这里会发生什么吗?

    1 回复  |  直到 14 年前
        1
  •  1
  •   Matthew Flaschen    14 年前

    这很简单。从输出中可以看到,第一次打开 “行,您位于第一个“v”元素。然后,调用readElementContentAsObject。返回v的文本内容, “将读卡器移过结束元素标记。”(共v个)。如果存在空白,则指向空白节点;如果不存在空白,则指向endelement节点(属于C)。当然,如果输出为空白,则不会打印输出。不管是哪种方法,然后执行read()并继续下一个元素。在非空白的情况下,您已经丢失了endelement。

    在其他情况下,问题更严重。当您执行C的readElementContentAsObject(称为C1)时,您将继续执行下一个C(C2)。然后你读一读,移动到c3,然后永远失去c2。

    我不会去修理 real code . 但很明显,你需要担心的是,将河流向前移动不止一个地方。这通常是循环错误的常见来源。