GData.SpreadsheetsライブラリのListFeedに潜む罠

Pocket

ほぼ1日ぐらいはまっていたのでメモ代わりに記録しておきます。

Googleの各種サービスはWeb APIを持っているだけでなく、.NET, Ruby, Python, Java等の各種言語向けSDKを用意しています。これを使ってGoogle Spreadsheetsを操作しようとして、ちょっとはまりました。

Spreadsheet APIのリファレンスには、spreadsheetを操作する2種類の方法を提供しています。ListFeed型(行単位処理)とCellFeed型(セル単位処理)です。

CellFeedは割と簡単で、指定した矩形範囲のセルを逐次処理させるのに向いている手段です。ただし、こちらにも若干の罠があって、デフォルトでは値の入っているセルのみしか操作できません。APIリファレンスのサンプルの書き方もそのようになっています。値の入っていないセルも捜査対象にするためには、CellQueryのReturnEmptyプロパティにReturnEmptyCells.yesを代入しておく必要があります。

using Google.GData;
using Google.GData.Client;
using Google.GData.Spreadsheets;

            CellQuery cq = new CellQuery(f.CellFeedLink); // f as WorksheetEntry
            cq.MaximumColumn = 5;
            cq.MaximumRow = 3;
            cq.ReturnEmpty = ReturnEmptyCells.yes; // 
            CellFeed cf = (serv.Query(cq));

上記のコードは、5×3のセルすべてにアクセスするためのコード例です。ReturnEmptyを指定しないと、値の入っていないセルはとばして結果を返してきます。

本題のListFeedの罠についてですが、まずAPIガイドのサンプルの一部を見てみます。

      AtomLink listFeedLink = worksheet.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

      ListQuery listQuery = new ListQuery(listFeedLink.HRef.ToString());
      ListFeed listFeed = service.Query(listQuery);

      ListEntry row = new ListEntry();
      row.Elements.Add(new ListEntry.Custom() { LocalName = "firstname", Value = "Joe" });
      row.Elements.Add(new ListEntry.Custom() { LocalName = "lastname", Value = "Smith" });
      row.Elements.Add(new ListEntry.Custom() { LocalName = "age", Value = "26" });
      row.Elements.Add(new ListEntry.Custom() { LocalName = "height", Value = "176" });

      service.Insert(listFeed, row);

このコードをまっさらなシートに対して実行をすると、API側がHTTPステータス400を返すので、GDataRequestExceptionが発生します。

何が問題なのかというと、ListEntry.CustomのプロパティであるLocalNameは「1行目のカラムの値」との対応付けがなされている前提のAPIだからです。上記の例だと、1行目に”firstname, lastname, age, height”という値が入ったセルが必要だということです。このことについて気付いたのは、値の入ったシートに対してListFeedで読み出しをしてみたら、1行分だけ少ない結果が返ってきたからです。CellFeedはそういう制約がないため、1行目を含めて内容の取得、変更等ができます。

あとになってガイドを読み返したら、以下のような説明がありました。

list row
Row of cells in a worksheet, represented as a key-value pair, where each key is a column name, and each value is the cell value. The first row of a worksheet is always considered the header row when using the API, and therefore is the row that defines the keys represented in each row.

強調部分に明記されていました(強調はこちらでしたもの、原文に強調はなし)。ドキュメントはちゃんと読みましょう、というお話でした。

By knok

I am a Debian Developer and a board member of Free Software Initiative (FSIJ).