1

私は、通常は非常に優れたオープン ソース プロジェクトであるExcel Data Readerの謎のバグに取り組んでいます。特定の OpenXML .xlsx スプレッドシートからの値の読み取りをスキップしています。

問題はReadSheetRow メソッドで発生しています (以下のデモ コード)。ソース 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>

問題を示すコード例:

Aは の後に出力され_xmlReader.Read()Bは の後に出力されReadToDescendantCは の後に出力されることに注意してください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、名前: 'xml'、空: False、値: 'version="1.0" encoding="UTF-8" standalone="yes"'
*A* NodeType: 要素、名前: 'ワークシート'、空: False、値: ''
*A* NodeType: 要素、名前: 'sheetData'、空: False、値: ''
*A* NodeType: 要素、名前: '行'、空: False、値: ''
*A* NodeType: 要素、名前: 'c'、空: False、値: ''
*B* NodeType: 要素、名前: 'v'、空: False、値: ''
*A* NodeType: EndElement、名前: 'c'、空: False、値: ''
*A* NodeType: 要素、名前: 'c'、空: True、値: ''
*B* NodeType: 要素、名前: 'c'、空: True、値: ''
...

空白のない XML のテスト結果:

*A* NodeType: XmlDeclaration、名前: 'xml'、空: False、値: 'version="1.0" encoding="UTF-8" standalone="yes"'
*A* NodeType: 要素、名前: 'ワークシート'、空: False、値: ''
*A* NodeType: 要素、名前: 'sheetData'、空: False、値: ''
*A* NodeType: 要素、名前: '行'、空: False、値: ''
*A* NodeType: 要素、名前: 'c'、空: False、値: ''
*B* NodeType: 要素、名前: 'v'、空: False、値: ''
*C* NodeType: EndElement、名前: 'c'、空: False、値: ''
*A* NodeType: 要素、名前: 'c'、空: True、値: ''
*B* NodeType: 要素、名前: 'c'、空: True、値: ''
...

パターンの変更は、XmlReader の移動ReadElementContentAsObject先または場所に問題があることを示しています。ReadToDescendant

ここで何が起こっているのか知っている人はいますか?

4

1 に答える 1

1

それはかなり単純です。出力からわかるように、最初に " B " 行にいるときは、最初の "v" 要素に配置されています。次に、ReadElementContentAsObject を呼び出します。これは v のテキスト コンテンツを返し、リーダーを要素の終了タグを超えて移動します」。(vの)。空白がある場合は空白ノードを、空白がない場合は (c の) EndElement ノードを指しています。もちろん、空白の場合、出力は印刷されません。いずれにしても、Read() を実行して次の要素に移動します。空白以外の場合は、EndElement が失われています。

他の状況では、問題はさらに悪化します。ac の ReadElementContentAsObject (c1 と呼ぶ) を実行すると、次の c (c2) に移動します。次に、読み取りを実行して c3 に移動し、c2 を永久に失います。

実際のコードを修正しようとするつもりはありません。しかし、複数の場所でストリームを前進させることで、何を心配する必要があるかは明らかです。これは、一般的なループ エラーの一般的な原因です。

于 2010-02-12T12:46:21.353 に答える