Open XML SDK 2.0 を使用して Excel 2010 ブック内のセルの値を取得する

Office Visual How To

概要:  Open XML SDK 2.0 の厳密に型指定されたクラスを使用すると、Microsoft Excel にドキュメントを読み込まなくても、Excel 2007 または Excel 2010 ドキュメント内のセルの値を取得できます。

適用対象: Excel 2007 | Excel 2010 | Office 2007 | Office 2010 | Open XML | PowerPoint 2010 | VBA | Word 2010

公開:   2010 年 8 月

提供元:   Ken Getz、MCW Technologies, LLC

概要

Open XML ファイル形式を使用すると、Excel ブック内の特定のセルに関する情報を取得できます。Open XML SDK 2.0 は、Open XML ファイル形式へのアクセスを簡素化する厳密に型指定されたクラスを追加します。この SDK は、ブックに関する情報を取得するタスクと、適切な XML コンテンツを検出するタスクを容易にします。この Visual How To に付属するコード サンプルでは、この SDK を使用して、このタスクを実行する方法について説明します。

コード化する

この Visual How To に付属するコード サンプルには、Excel 2007 または Excel 2010 ブックの指定されたシート内の指定されたセルの値を取得するのに必要なコードが含まれています。

参照のセットアップ

Open XML SDK 2.0 のコードを使用するには、プロジェクトにいくつかの参照を追加する必要があります。サンプル プロジェクトには、これらの参照が含まれていますが、独自のコードを使用する場合は、次のアセンブリを明示的に参照する必要があります。

  • WindowsBase ─ 作成するプロジェクトの種類によっては、この参照がユーザーのために設定されている場合があります。

  • DocumentFormat.OpenXml ─ Open XML SDK 2.0 によってインストールされます。

また、次の using/Imports ステートメントをコード ファイルの先頭に追加する必要があります。

Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

プロシージャの確認

XLGetCellValue プロシージャは、次の値を示す 3 つのパラメーターを受け取ります。

  • 確認するドキュメントの名前 (文字列)。

  • 確認するシートの名前 (文字列)。

  • 値を取得するセル番地 (A1、B12 など) (文字列)。

Public Function XLGetCellValue(ByVal fileName As String,
  ByVal sheetName As String, ByVal addressName As String) As String
public static string XLGetCellValue(string fileName, 
  string sheetName, string addressName)

このプロシージャは、指定されたセルの値が見つかると、その値を返します。このプロシージャを呼び出すには、次のコード例に示すように、パラメーター値をすべて渡します。

Const fileName As String = "C:\temp\GetCellValue.xlsx"
Dim value As String = XLGetCellValue(fileName, "Sheet1", "B3")
const string fileName = @"C:\temp\GetCellValue.xlsx";
string value = XLGetCellValue(fileName, "Sheet1", "A1");

セルへのアクセス

次のコード例では、コードを開始する最初の処理として、戻り値を含む変数を作成します。ここでは、NULL の結果を想定しています。

Dim value as String = Nothing
string value = null;

次に、SpreadsheetDocument.Open メソッドを使用してドキュメントを開き、ドキュメントを読み取り専用で開く必要があることを指定します (最後の false パラメーターで指定)。さらに、ドキュメントの WorkbookPart プロパティを使用して、ブック パーツへの参照を取得します。

Using document As SpreadsheetDocument =
  SpreadsheetDocument.Open(fileName, False)

  Dim wbPart As WorkbookPart = document.WorkbookPart
  ' Code removed here…
End Using
using (SpreadsheetDocument document = 
  SpreadsheetDocument.Open(fileName, false))
{
    WorkbookPart wbPart = document.WorkbookPart;
    // Code removed here…
}

要求されたセルを見つけるために、まず、シート名を基に、シートへの参照を取得する必要があります。そのためには、ブック パーツの Workbook プロパティの、シートの種類の子をすべて検索して、各シートの Name プロパティを調べる必要があります。この検索では、ブックの関係を調べるだけで、ワークシート パーツを実際に検出するわけではありません。つまり、シートの名前や ID などの情報を含む Sheet への参照のみを検出します。この検索は、LINQ クエリを使用すると、最も簡単に実行できます。

Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)(). 
  Where(Function(s) s.Name = sheetName).FirstOrDefault()

If theSheet Is Nothing Then
  Throw New ArgumentException("sheetName")
End If
Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
  Where(s => s.Name == sheetName).FirstOrDefault();
if (theSheet == null)
{
  throw new ArgumentException("sheetName");
}

FirstOrDefault メソッドは、最初に一致する参照 (この例ではシート) または NULL 参照 (一致が見つからない場合) を返します。NULL 参照かどうかを確認し、無効なシート名が渡されている場合は、例外をスローします。

シートに関する情報を取得したので、次は、対応するワークシート パーツへの参照を取得する必要があります。取得したシート情報には Id プロパティが含まれています。この Id プロパティを使用して、対応する WorksheetPart への参照を取得します。そのためには、WorkbookPart の GetPartById プロパティを呼び出します。

Dim wsPart As WorksheetPart =
  CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
WorksheetPart wsPart = 
  (WorksheetPart)(wbPart.GetPartById(theSheet.Id));

名前が指定されたシートやセルを見つける場合と同様に、Descendants メソッドを使用して最初の一致 (Reference プロパティと指定された addressName パラメーターが一致する) を探します。このメソッドを呼び出した後、theCell という名前の変数には、セルへの参照または NULL 参照が含まれます。

Dim theCell As Cell = wsPart.Worksheet.Descendants(Of Cell).
  Where(Function(c) c.CellReference = addressName).FirstOrDefault
Cell theCell = wsPart.Worksheet.Descendants<Cell>().
  Where(c => c.CellReference == addressName).FirstOrDefault();

値の取得

この時点で、変数 theCell には、NULL 参照が含まれるか、要求されたセルへの参照が含まれています。セルの Open XML コンテンツ (つまり、theCell.OuterXml) を調べてみると、次のコード例に示すような XML が見つかります。

    <x:c r="A1">
    <x:v>12.345000000000001</x:v>
    </x:c>

InnerText プロパティには、セルの内容が含まれています。したがって、次のコード ブロックでは、この値を取得します。

If theCell IsNot Nothing Then
  value = theCell.InnerText
  ' Code removed here…
End If
if (theCell != null)
{
  value = theCell.InnerText;
  // Code removed here…
}

次に、サンプル プロシージャは値を解釈する必要があります。ただし、このサンプルでは、数値、日付値、文字列値、および Boolean 値を処理しています。このサンプルは必要に応じて拡張することもできます。Cell タイプには、セル内のデータの型を指定する DataType プロパティがあります。DataType プロパティの値は、データ型が数値および日付型の場合は NULL で、文字列の場合は CellValues.SharedString、Boolean 値の場合は CellValues.Boolean です。DataType プロパティが NULL の場合は、セルの値 (数値) のみを返します。NULL 以外の場合は、データ型に応じて条件分岐して処理を続行します。

If theCell.DataType IsNot Nothing Then
  Select Case theCell.DataType.Value
  ' Code removed here…
  End Select
End If
if (theCell.DataType != null)
{
  switch (theCell.DataType.Value)
    {
    // Code removed here…
    }
}

DataType プロパティに CellValues.SharedString が含まれている場合は、単一の SharedStringTablePart への参照を取得する必要があります。

Dim stringTable = wbPart.
  GetPartsOfType(Of SharedStringTablePart).FirstOrDefault()ws.Save()
var stringTable = 
  wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();

次に、文字列テーブルが存在する場合は、指定されたインデックスの位置で検出される要素の InnerText プロパティを返します (最初に値プロパティが整数に変換されます)。

注意

文字列テーブルが存在しない場合は、ブックは壊れているため、文字列ではなく、文字列テーブルへのインデックスを返します。

If stringTable IsNot Nothing Then
  value = stringTable.SharedStringTable.
    ElementAt(Integer.Parse(value)).InnerText
End If
if (stringTable != null)
{
  value = stringTable.SharedStringTable.
    ElementAt(int.Parse(value)).InnerText;
}

DataType プロパティに CellValues.Boolean が含まれている場合は、セル値から検出される 0 または 1 を適切なテキスト文字列に変換します。

Case CellValues.Boolean
  Select Case value
    Case "0"
      value = "FALSE"
    Case Else
      value = "TRUE"
  End Select
case CellValues.Boolean:
  switch (value)
  {
    case "0":
      value = "FALSE";
      break;
    default:
      value = "TRUE";
      break;
  }

最後に、このプロシージャは、要求された情報を含む変数 value を返します。

サンプル プロシージャ

次のコード例に、完全なプロシージャを示します。

Public Function XLGetCellValue(ByVal fileName As String,
  ByVal sheetName As String, ByVal addressName As String) As String
  Dim value As String = Nothing

  Using document As SpreadsheetDocument =
    SpreadsheetDocument.Open(fileName, False)

    Dim wbPart As WorkbookPart = document.WorkbookPart

    ' Find the sheet with the supplied name, and then use that 
    ' Sheet object to retrieve a reference to the appropriate 
    ' worksheet.
    Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)().
      Where(Function(s) s.Name = sheetName).FirstOrDefault()

    If theSheet Is Nothing Then
      Throw New ArgumentException("sheetName")
    End If

    ' Retrieve a reference to the worksheet part, and then use its 
    ' Worksheet property to get a reference to the cell whose address 
    ' matches the address you supplied:
    Dim wsPart As WorksheetPart =
      CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
    Dim theCell As Cell = wsPart.Worksheet.Descendants(Of Cell).
      Where(Function(c) c.CellReference = addressName).FirstOrDefault

    ' If the cell does not exist, return an empty string.
    If theCell IsNot Nothing Then
      value = theCell.InnerText

      ' If the cell represents an numeric value, you are done. 
      ' For dates, this code returns the serialized value that 
      ' represents the date. The code handles strings and Booleans
      ' individually. For shared strings, the code looks up the 
      ' corresponding value in the shared string table. For Booleans, 
      ' the code converts the value into the words TRUE or FALSE.
      If theCell.DataType IsNot Nothing Then
        Select Case theCell.DataType.Value
          Case CellValues.SharedString
            ' For shared strings, look up the value in the shared 
            ' strings table.
            Dim stringTable = wbPart.
              GetPartsOfType(Of SharedStringTablePart).FirstOrDefault()
            ' If the shared string table is missing, something is wrong.
            ' Return the index that you found in the cell.
            ' Otherwise, look up the correct text in the table.
            If stringTable IsNot Nothing Then
              value = stringTable.SharedStringTable.
                ElementAt(Integer.Parse(value)).InnerText
            End If
          Case CellValues.Boolean
            Select Case value
              Case "0"
                value = "FALSE"
              Case Else
                value = "TRUE"
            End Select
        End Select
      End If
    End If
  End Using
  Return value
End Function
public static string XLGetCellValue(
  string fileName, string sheetName, string addressName)
{
  string value = null;

  using (SpreadsheetDocument document = 
    SpreadsheetDocument.Open(fileName, false))
  {
    WorkbookPart wbPart = document.WorkbookPart;

    // Find the sheet with the supplied name, and then use that Sheet
    // object to retrieve a reference to the appropriate worksheet.
    Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
      Where(s => s.Name == sheetName).FirstOrDefault();

    if (theSheet == null)
    {
      throw new ArgumentException("sheetName");
    }

    // Retrieve a reference to the worksheet part, and then use its 
    // Worksheet property to get a reference to the cell whose 
    // address matches the address you supplied:
    WorksheetPart wsPart = 
      (WorksheetPart)(wbPart.GetPartById(theSheet.Id));
    Cell theCell = wsPart.Worksheet.Descendants<Cell>().
      Where(c => c.CellReference == addressName).FirstOrDefault();

    // If the cell does not exist, return an empty string:
    if (theCell != null)
    {
      value = theCell.InnerText;

      // If the cell represents a numeric value, you are done. 
      // For dates, this code returns the serialized value that 
      // represents the date. The code handles strings and Booleans
      // individually. For shared strings, the code looks up the 
      // corresponding value in the shared string table. For Booleans, 
      // the code converts the value into the words TRUE or FALSE.
      if (theCell.DataType != null)
      {
        switch (theCell.DataType.Value)
        {
          case CellValues.SharedString:
            // For shared strings, look up the value in the shared 
            // strings table.
            var stringTable = wbPart.
              GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
            // If the shared string table is missing, something is 
            // wrong. Return the index that you found in the cell.
            // Otherwise, look up the correct text in the table.
            if (stringTable != null)
            {
              value = stringTable.SharedStringTable.
                ElementAt(int.Parse(value)).InnerText;
            }
            break;

          case CellValues.Boolean:
            switch (value)
            {
              case "0":
                value = "FALSE";
                break;
              default:
                value = "TRUE";
                break;
            }
            break;
        }
      }
    }
  }
  return value;
}
手順

この Visual How To に付属するサンプルには、Excel 2007 または Excel 2010 ブック内の指定されたシートに含まれる特定のセルから値を取得するコードが含まれています。このコード サンプルを使用するには、「関連情報」セクションのリンクから入手できる Open XML SDK 2.0 をインストールします。また、このサンプルでは、Open XML SDK 2.0 のコード例の一部として含まれるコードも使用しています。「関連情報」セクションには、コード例の全セットへのリンクも含まれていますが、このサンプルについては、サンプル コードをダウンロードおよびインストールしなくても使用できます。

このサンプル アプリケーションは、指定されたドキュメント内のいくつかのセルから値を取得します。このサンプルでは、この処理を行うために XLGetCellValue メソッドを呼び出します。このメソッドは、指定されたセルの値を文字列として返します。したがって、このメソッドを呼び出したコードでは、文字列値を解釈する必要があります。このメソッドの呼び出しは、次のコード例のようになります。

Const fileName As String = "C:\temp\GetCellValue.xlsx"
Dim value As String = XLGetCellValue(fileName, "Sheet1", "A1")
Console.WriteLine(value)
value = XLGetCellValue(fileName, "Sheet1", "A2")
Console.WriteLine(value)
value = XLGetCellValue(fileName, "Sheet1", "A3")
Console.WriteLine(value)
value = XLGetCellValue(fileName, "Sheet1", "A4")
Console.WriteLine(value)
const string fileName = @"C:\temp\GetCellValue.xlsx";
string value = XLGetCellValue(fileName, "Sheet1", "A1");
Console.WriteLine(value);
value = XLGetCellValue(fileName, "Sheet1", "A2");
Console.WriteLine(value);
value = XLGetCellValue(fileName, "Sheet1", "A3");
Console.WriteLine(value);
value = XLGetCellValue(fileName, "Sheet1", "A4");
Console.WriteLine(value);

セルとセル値がどのような方法で Excel に格納されるかを理解することが重要です。図 1 にサンプル ブックを示します。このブックには 4 つのセルに 4 種類の値が含まれています (サンプル アプリケーションでは、このブックは既に作成済みで、C:\temp\GetCellValue.xlsx として格納済みであると前提)。Open XML SDK 2.0 の tool ディレクトリには、OpenXmlSdkTool.exe という名前の便利なアプリケーションがあります (図 2 を参照)。このツールを使用すると、ドキュメントを開いて、ドキュメントの各種パーツと各パーツの階層構造を表示できます。図 2 にテスト ドキュメントを示します。左側のウィンドウには、ドキュメントがワークシート ノードまで展開されています。右側のウィンドウには、パーツの XML と、そのパーツのコンテンツの生成に使用できる C# コードが反転して表示されています。

図 1. Excel ブックのサンプル データ

Excel ブックのサンプル データ

図 2 に、Open XML SDK 2.0 に付属する OpenXmlSdkTool.exe アプリケーションを示します。

図 2. Open XML SDK 2.0 Productivity Tool

Open XML SDK 2.0 生産性ツール

図 2 に示す左側のウィンドウ (パーツの階層) と XML コンテンツを調べるには、この Visual How To のコードを理解できる必要があります。

  • 右側のハイライトされた XML コンテンツを見ると、マークアップにセル番地への参照が含まれていることがわかります。つまり、セル番地を指定すると、必要なコンテンツを見つけることができます。

  • 必要なセルを見つけるには、まず、そのセルが含まれるシートを探します。

この Productivity Tool ツールを使用しても確認できないことがあります。それは、名前が指定されたシートへの参照を取得する場合です。この場合は、まず、ブック内のすべてのシートと名前を照合して一致を見つける必要があります。さらに、シートとシートの ID を基に、ワークシート パーツを取得する必要があります。サンプル コードでは、こうした処理を行っています。

Excel では、ワークシート内で使用される各文字列を取り込むことを目的としてのみ、パーツを保持します。一意の文字列はそれぞれ、このテーブルに 1 回表示され、Excel は、このテーブルへのインデックスを、文字列が含まれる各セルに格納します。CellValues.SharedString の DataType プロパティに基づいて、対応する共有文字列を共有文字列テーブル パーツからインデックスによって取得する必要があります。図 3 に、サンプル ブックの共有文字列テーブルの Open XML SDK 2.0 Productivity Tool 表現を示します。この例には、インデックス 0 および 1 の 2 つの文字列が含まれています。

図 3. サンプル ワークシートの共有文字列テーブルを示す Open XML SDK 2.0 Productivity Tool

生産性ツールで共有文字列テーブルが表示されている
ビデオ

ビデオの視聴

長さ: 00:10:46

クリックしてコードを取得

コードを取得する

関連情報

著者について
Ken Getz 氏は、MCW Technologies のシニア コンサルタント。『ASP.NET Developers Jumpstart』(Addison-Wesley 刊、2002 年)、『 Access Developer's Handbook』(Sybex 刊、2001 年)、および『VBA Developer's Handbook, 2nd Edition』(Sybex 刊、2001 年) の共著者でもあります。