AppsのスプレッドシートデータをApp Engineから読み取る

2013年11月13日(水)
清野 克行

2.Drive上のSpreadSheetをApp Engineから読み取り画面表示する

ここまでで、ExcelシートとSpreadsheetのアップロードおよびファイル変換についての説明は終わり、次に本連載メインテーマのApp EngineからのSpreadsheet読み取りについて入っていきます。

ただし、ここからはJavaやJavascriptを使用したプログラミングの内容になっていきますので、ある程度プログラミング経験があることを前提とした内容になっていきます。

図9:Drive上のスプレッドデータをApp Engineから読み取る(クリックで拡大)

ここで紹介するApp EngineからのSpreadsheet読み取りでは、参照画面のボタンクリックから起動されるサーブレットによってビーンズをインスタンス化し、その中のメソッドによって実行されます(図9)。読み取られたSpreadデータはブラウザに画面表示されますが、今回は簡易形式での画面表示を紹介し、次回の連載でスプレッドシート風のグリッド表示について紹介します。

2.1 デプロイメントディスクリプタ(web.xml)を記述する

リスト1 デプロイメントディスクリプタ(web.xml)

01<?xml version="1.0" encoding="utf-8" standalone="no"?>
03  <servlet>
04    <servlet-name>spread</servlet-name>
05    <servlet-class>com.google.gdata.SpreadServlet</servlet-class>
06  </servlet>
07  <servlet-mapping>
08    <servlet-name>spread</servlet-name>
09    <url-pattern>/spread</url-pattern>
10  </servlet-mapping>
11        :
12  <welcome-file-list>
13    <welcome-file>index.htm</welcome-file>
14  </welcome-file-list>
15</web-app>

ブラウザからの表示リクエストはデプロイメントディスクリプタ(web.xml)の記述にしたがってサーブレットを呼び出します。この辺りは問題ないでしょう。

2.2 サーブレットを記述する

リスト2 サーブレット(SpreadServlet.java)

01package com.google.gdata;
02import java.io.IOException;
03import java.io.PrintWriter;
04import javax.servlet.http.*;
05import com.google.gdata.util.ServiceException;
06 
07@SuppressWarnings("serial")
08public class SpreadServlet extends HttpServlet {
09  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
10    resp.setContentType("text/plain");
11    resp.setContentType("text/html; charset=utf-8");
12    PrintWriter out = resp.getWriter();
13    String mode = req.getParameter("mode"); //呼び出すビーンズメソッドを指定
14    String sheet = req.getParameter("sheet");   //読み取るシート名を指定
15    SpreadBean ss = new SpreadBean();           //スプレッドシート処理のビーンズをインスタンス化
16    if (mode.equals("getspreadallshopym")) {        //modeパラメータでビーンズメソッド指定
17      String rv = ss.getSpreadAllShopYm(sheet);
18      //ワークシート一覧表示のビーンズメソッド呼び出し
19      out.println(rv);
20  } else if (mode.equals("getspreadshopym")) {
21      String shop = req.getParameter("shop");
22      String rv = ss.getSpreadShopYm(sheet, shop);
23      //ワークシート店舗別表示のビーンズメソッド呼び出し
24      out.println(rv);
25    }
26  }
27}

呼び出されたサーブレットはmodeパラメータで指定された値によってビーンズメソッドを呼び出します。

2.3 ワークシート一覧表示のビーンズメソッドを記述する

ここで見ていくビーンズメソッドでは、ワークシートに書き込まれたデータを一覧表示するもので、Spreadsheetの検索表示では一般的なものです。

リスト3 ビーンズ(SpreadBean.java)

01package com.google.gdata;
02   
03import java.io.IOException;
04import java.net.URL;
05import java.text.DateFormat;
06import java.text.SimpleDateFormat;
07import java.util.ArrayList;
08import java.util.Date;
09import java.util.Iterator;
10import java.util.List;
11   
12import com.google.appengine.api.datastore.DatastoreService;
13import com.google.appengine.api.datastore.DatastoreServiceFactory;
14import com.google.appengine.api.datastore.Entity;
15import com.google.appengine.api.datastore.EntityNotFoundException;
16import com.google.appengine.api.datastore.Key;
17import com.google.appengine.api.datastore.KeyFactory;
18import com.google.appengine.api.datastore.Query;
19import com.google.appengine.api.datastore.Query.Filter;
20import com.google.appengine.api.datastore.Query.FilterOperator;
21import com.google.gdata.client.spreadsheet.FeedURLFactory;
22import com.google.gdata.client.spreadsheet.ListQuery;
23import com.google.gdata.client.spreadsheet.SpreadsheetQuery;
24import com.google.gdata.client.spreadsheet.SpreadsheetService;
25import com.google.gdata.data.spreadsheet.CustomElementCollection;
26import com.google.gdata.data.spreadsheet.ListEntry;
27import com.google.gdata.data.spreadsheet.ListFeed;
28import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
29import com.google.gdata.data.spreadsheet.SpreadsheetFeed;
30import com.google.gdata.data.spreadsheet.WorksheetEntry;
31import com.google.gdata.data.spreadsheet.WorksheetFeed;
32import com.google.gdata.util.AuthenticationException;
33import com.google.gdata.util.ServiceException;
34import com.google.appengine.api.datastore.Query.FilterPredicate;
35import com.google.appengine.api.datastore.PreparedQuery;
36 
37public class SpreadBean {
38  public String getSpreadAllShopYm(String sheet) {
39    String appliName = "cyberspace-SpreadsheetSearch-1";
40    String user = "xxxxxxxxxxxxxxxxxxx";
41    String pass = "xxxxxxxxxxxxxxxxx";
42    try {
43      ///////////////////////////////////////////////////////
44      //        [I] 認証処理
45      SpreadsheetService spreadsheetservice = new SpreadsheetService(appliName);
46      spreadsheetservice.setUserCredentials(user, pass);
47      ///////////////////////////////////////////////////////
48      //        [II] 検索対象のスプレッドシートを特定
49      FeedURLFactory feedurlfactory = FeedURLFactory.getDefault();
50      SpreadsheetQuery spreadsheetquery
51        = new SpreadsheetQuery(feedurlfactory.getSpreadsheetsFeedUrl());
52      spreadsheetquery.setTitleQuery(sheet);
53      SpreadsheetFeed spreadsheetfeed
54        = spreadsheetservice.query(spreadsheetquery, SpreadsheetFeed.class);
55      SpreadsheetEntry spreadsheetentry = spreadsheetfeed.getEntries().get(0);
56      ///////////////////////////////////////////////////////
57      //        [III] 検索対象のワークシートを特定
58      WorksheetEntry worksheetentry = spreadsheetentry.getDefaultWorksheet();
59      ///////////////////////////////////////////////////////
60      //        [IV] クエリでワークシート内の検索
61      ListQuery listquery = new ListQuery(worksheetentry.getListFeedUrl());
62      ListFeed listfeed = spreadsheetservice.query(listquery, ListFeed.class);
63      List<ListEntry> datalist = lisfFeed.getEntries();
64      ///////////////////////////////////////////////////////
65      //        [V] ワークシートデータをJSONフォーマットにする
66      String rv = "{ \"wsdat\":[";
67      for (Iterator<ListEntry> it = datalist.iterator(); it.hasNext();) {
68        ListEntry listentry = (ListEntry) it.next();
69        rv +=  "{\"cdat\": \"" + listentry.getPlainTextContent() + "\"},";
70      }
71      rv = rv.substring(0, rv.length() - 1) + "]}";
72      return rv;
73    } catch (AuthenticationException e) {
74      e.printStackTrace();
75      return "参照不成功 :" + e;
76    } catch (IOException e) {
77      e.printStackTrace();
78      return "参照不成功 :" + e;
79  } catch (ServiceException e) {
80  e.printStackTrace();
81  return "参照不成功 :" + e;
82  }
83 }
84  
85}

[I] 認証処理

1String appliName = "cyberspace-SpreadsheetSearch-1";
2String user = "xxxxxxxxxxxxxxxxxxx";
3String pass = "xxxxxxxxxxxxxxxxx";
4 
5SpreadsheetService spreadsheetservice = new SpreadsheetService(appliName);        //(1)
6spreadsheetservice.setUserCredentials(user, pass);                                //(2)

処理の最初はアプリケーション名の指定と、ユーザ名とパスワードによる認証処理です。

(1)アプリケーション名はSpreadsheetと接続する場合の識別用に使用されます。

(2)ユーザ名とパスワードによる認証はsetUserCredentialsメソッドで行いますが、Googleアカウントのメールアドレスとパスワードを使用します。 実システムでは画面からGoogleアカウントとパスワードを入力して、ユーザの認証を行うようにするのが良いでしょう。

[II] 検索対象のスプレッドシートを特定する

1FeedURLFactory feedurlfactory = FeedURLFactory.getDefault();                    (1)
2SpreadsheetQuery spreadsheetquery
3  = new SpreadsheetQuery(feedurlfactory.getSpreadsheetsFeedUrl());          (2)
4spreadsheetquery.setTitleQuery(sheet);                                      (3)
5SpreadsheetFeed spreadsheetfeed
6  = spreadsheetservice.query(spreadsheetquery, SpreadsheetFeed.class);      (4)
7SpreadsheetEntry spreadsheetentry = spreadsheetfeed.getEntries().get(0);        (5)

(1)では、Spreadsheetsサーバで使用される フィードURLのデフォルトインスタンスを生成します。
フィード(Feed)には幾つかの意味がありますが、ここでは、Webサイトから受信可能なデータと考えれば良いでしょう。

(2)feedurlfactory.getSpreadsheetsFeedUrl() では認証によってアクセス可能なすべてのSpreadsheetに対するフィードを受け取ることができるURLを取得し、SpreadsheetQueryでは取得したフィードURLを引数にして、Spreadsheetを検索するためのqueryを生成します。

(3)生成されたqueryにスプレッドシート名(sheet)を引数にしたフルテキスト検索を構成します。
ここで注意が必要なのは、Drive上には同じ名前のSpreadsheetファイルを複数配置することができるということです。また、同じ名前のspreadsheetはDrive上のフォルダが同じでも違っていても、すべて(3)のフル検索の対象になります。

例えば、図10にはSalesフォルダ内にファイル名201304のシートが1つあり、図11にはtestフォルダ内に同じ201304のシートが2つありますが、これらはすべて検索の対象になります。

図10:Salesフォルダ内の201304シート(クリックで拡大)
図11:Testフォルダ内の201304シート(クリックで拡大)

(4)では(3)で生成したspreadsheetqueryを引数にクエリを実行し、クエリ内容に一致するエントリを含んだフィードを取得します。

(5)spreadsheetfeed.getEntries() でフィード内のスプレッドシートエントリのリストを返しますが、上で見たように同じ名前のスプレッドシートを複数作成することもできるので、get(0) で1つのスプレッドシートを特定します。ただここで、複数の同じ名前のシートの内、どのシートがget(0)で選択されるのかが問題ですが、図10、図11の右端に表示されているLAST MODIFIED(最終更新日)が最も新しいシートが選択されます。つまりgetメソッドの引数は更新日の新しい方から、0,1,2…となっている訳です。
ただし、更新の度に選択されるシートが変わってしまうのでは正確な処理を行うことはできません。したがって、Drive上でのスプレッドシート名はすべて一意(unique)にしておくべきです。

[III] 検索対象のワークシートを特定する

1WorksheetEntry worksheetentry = spreadsheetentry.getDefaultWorksheet();         (1)

ここまでの処理で、検索対象は1枚のスプレッドシートに特定されましたが、最後にワークシートの特定を行います。

図12:スプレッドシート内のワークシート(クリックで拡大)

Excelでは1つのシートファイルに、タブ設定によって複数の入力シートを保持できますが、Drive上のSpreadsheet でも同様に複数のワークシートを保持することができます。したがって、複数ワークシートから1つを特定することが必要になってきますが、これを行っているのが(1)です。getDefaultWorksheet() は複数のワークシートから一番上に表示されているシート(図12のSheet1)を選択して返します。

[IV] クエリでワークシートデータの検索

ここまでで、複数のSpreadsheetから、幾つかの処理を経て検索対象のワークシートを特定する処理を見てきましたが、これでようやくワークシートデータの検索ができるようになります。

1ListQuery listQuery = new ListQuery(worksheetentry.getListFeedUrl());           (1)
2ListFeed listFeed = spreadsheetservice.query(listQuery, ListFeed.class);            (2)
3List<ListEntry> dataList = listFeed.getEntries();                             (3)

(1)worksheetentry.getListFeedUrl() でワークシートのリストフィードのURLを取得し、それを引数にした ListQuery の生成でワークシートに対する行ベースのクエリを構成します。

(2)ターゲットサービスへのクエリを実行し、クエリ結果に一致するリストフィード(listFeed)を返します。

(3)リストフィード内のエントリを行単位のリストで返します。

[V] ワークシートデータをJSONフォーマットで返す

1String rv = "{ \"wsdat\":[";
2for (Iterator<ListEntry> it = dataList.iterator(); it.hasNext();) {
3  ListEntry listEntry = (ListEntry) it.next();
4  rv +=  "{\"cdat\": \"" + listEntry.getPlainTextContent() + "\"},";
5}
6rv = rv.substring(0, rv.length() - 1) + "]}";
7return rv;

最後に、行単位のワークシートデータをJSONフォーマットに組み立てでクライアントに送りますが、この部分は一般的な繰り返し(iterate)処理ですので、問題ないでしょう。

有限会社サイバースペース
慶應義塾大学工学部電気科卒。日本IBM、日本HPなどにおいて、製造装置業を中心とした業務系/基幹業務系システムのSE/マーケティングや、3階層C/Sアーキテクチャによる社内業務システム開発などに携わる。現在は、Ajax/Web 2.0関連のセミナー講師/コンサルティング、書籍執筆などを行っている。情報処理学会会員。http://www.at21.net/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています