Open XML SDK で Excel シートを読み込む

Excel 2007 以降では、既定のデータ保存形式が Open XML (Office Open XML, OOXML とも) となりました。
今回は、Open XML 形式で保存された Excel シート (スプレッドシート) のセルの値を取得する方法について記述します。

Open XML 形式の Excel ファイル (.xlsx) は、各データが格納された XML ファイルの集合を ZIP 圧縮したものです。
実際に Excel ファイルの拡張子を .zip に変更してみると、XML ファイルで構成されていることがわかります。

Book1.xlsx.zip

これらの XML ファイル群は、次のようなデータモデルを形成しています (一部のみを記述)。

Spreadsheet 論理データモデル

各ワークシートの ID・名前や順序は workbook.xml に格納されています。
初めにここから ID を取得することにより、ワークシートの実体の場所 (sheet1.xml など) を求めることができます。

また、Excel を利用してデータを保存すると、セルの値が文字列の場合、その実体は sharedStrings.xml に格納されます。
CellValue にはそのインデックスが格納されます。
(数値の場合、CellValue に直接格納されます。)

このような一連の論理データにアクセスするために、Open XML SDK 2.5 が提供されています。
OpenXMLSDKV25.msi には DLL ファイルが含まれており、これをインストールすれば最低限は使えるようになりますが、
Open XML SDK Productivity Tool (OpenXMLSDKToolV25.msi) もインストールしておくとよいでしょう。

Open XML SDK Productivity Tool を使うと、Excel ファイル内の XML を改行・インデント付きで表示できます。
また、現在のデータを作成するための C# のコードも自動生成されます。

Open XML SDK Productivity Tool

 

さて、Open XML SDK で提供されているのは上の図で示したような論理的データへのアクセスであり、概念的な操作ができません。
開発者の観点からすると、例えば、

var valueB3 = sheet["B3"];

または

var valueB3 = sheet[1, 2];

のような方法でセルの値を取得したいところです。

そこで、次のようなラッパー クラスを作成しました。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace OpenXmlSample
{
    public class ExcelSheet
    {
        Dictionary<string, string> sheetDataCache;

        public string this[string cellReference]
        {
            get { return sheetDataCache.ContainsKey(cellReference) ? sheetDataCache[cellReference] : null; }
        }

        public void Load(string filePath)
        {
            using (var document = SpreadsheetDocument.Open(filePath, false))
            {
                var sharedStringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;
                var sharedStringsCache = sharedStringTable.Elements<SharedStringItem>()
                    .Select((si, i) => new { i, Text = si.Elements<Text>().FirstOrDefault() })
                    .ToDictionary(x => x.i, x => x.Text != null ? x.Text.Text : null);

                Func<Cell, string> getValue = cell =>
                    cell.DataType == null ? cell.CellValue.Text :
                    cell.DataType.Value == CellValues.SharedString ? sharedStringsCache[Convert.ToInt32(cell.CellValue.Text)] :
                    cell.CellValue.Text;

                // ここでは、最初のシートを参照します。
                var sheet = document.WorkbookPart.Workbook.Sheets.Elements<Sheet>().First();
                var worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheet.Id);
                var sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
                sheetDataCache = sheetData.Descendants<Cell>()
                    .ToDictionary(c => c.CellReference.Value, getValue);
            }
        }
    }
}


Load メソッドの最初で、SharedStringTable の値をキャッシュしています。

sharedStringTable.ChildElements[12]

のようにインデックスを指定してアクセスすることもできるのですが、
Open XML SDK の内部では辞書的なインデックスが作成されずに都度列挙しているようで、
この方法ではデータの規模が大きくなると取得に時間がかかってしまいます。
そこで、データの列挙を 1 回で済ますために、最初に辞書式のキャッシュを作っておきます。

同様にして、SheetData を辞書式にキャッシュします。
CellReference というのは、"A1" のようなセルの名前 (座標) を表します。

このようなラッパー クラスを作っておくと、次のようにしてセルの値を取得できます。


using System;

namespace OpenXmlSample
{
    static class Program
    {
        static void Main(string[] args)
        {
            var sheet = new ExcelSheet();
            sheet.Load("Book1.xlsx");

            var person = new Person
            {
                Id = sheet["A3"].To<int>(),
                Name = sheet["B3"], 
                Salary = sheet["C3"].To<decimal>(),
            };
        }

        public static T To<T>(this object value)
        {
            return (T)Convert.ChangeType(value, typeof(T));
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Salary { get; set; }
    }
}


ちなみに、ClosedXML というものが存在するようです。

バージョン情報
Excel 2010
.NET Framework 4

参照
Open XML SDK 2.5
Open XML SDK 2.0 を使用して Excel 2010 ブック内のセルの値を取得する
Open XML を使った Office データの作成
ClosedXML – The easy way to OpenXML

Excel 方眼紙でドット絵を描く
Excel ファイルから画像を自動で取り出す

カテゴリー: .NET Framework. タグ: , . 3 Comments »

コメント / トラックバック3件 to “Open XML SDK で Excel シートを読み込む”

  1. Excel 方眼紙でドット絵を描く | Do Design Space Says:

    […] 参照 Dot Excel (CodePlex) Open XML SDK 2.5 Open XML for Office developers Open XML SDK で Excel シートを読み込む […]

  2. てst Says:

    バグがあったり・・・

  3. Excel ファイルから画像を取り出す | Do Design Space Says:

    […] 参照 ZipFile クラス PowerShellでOpen XML形式のMS Officeに保存された画像ファイルを抽出する Excel 方眼紙でドット絵を描く Open XML SDK で Excel シートを読み込む […]


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。