Powered by SmartDoc

解決法いろいろ

カンマ区切りテキスト

一つのデータを一行に格納します。データの中の各項目は、「,」で区切ります。つまりリスト[カンマ区切りデータ]のようなCSV形式です。

カンマ区切りデータ
コーヒー, 120, 主食
チョコレート, 180, オヤツ
ノート, 150, いつでもどこでもアイデアを記録

この利点は、形式が単純なのでエディタなどで編集できるというだけでなく、Excelなどのアプリケーションとも簡単にデータをやりとりできることが挙げられます。プログラム的にもjava.util.StringTokenizer等で切り出すことで、簡単に実装できます。

欠点は、まず第一に単純なテキストデータしか入れることが出来ないことが挙げられます。簡単な拡張としてカンマや改行を含んだりするデータを格納できるようにするだけでも、エスケープなどの小細工が必要になり、一気に複雑度が上がります。また、今後データの項目が増えていくとカンマの数が多くなるわけですが、極端な例として100個も項目があるようなデータはもはやエディタでの編集は困難です。

さらに、最大の欠点は構造を持ったデータが入れられないことです。データに構造を持たせることが出来ないということは、単純なデータしか格納できないことを意味しています。日々アプリケーションに改良を加えることによってデータの内容はどうしても複雑化して行くものですが、この形式ではすぐに限界につき当たってしまい、今後の拡張が困難になってしまいます。

以上の考察から、カンマ区切りテキスト形式は、表のような2次元の単純なデータには低コストで実装できるデータ形式ですが、今後拡張の可能性のあるデータや、 複雑な構造を持ったデータには適用はしない方が無難であると言えます。もし採用するとしても、使い捨てのプログラムでない限り、他の形式に乗り換えることが出来るような設計をするべきだと思います。

オブジェクト直列化

JAVAのオブジェクトを言語に標準搭載のシリアライゼーション(直列化)により、シリアライズしてファイルに格納します。データの読み込みはリスト[オブジェクト読み込み]のようです

オブジェクト読み込み
ObjectInputStream in
    = new ObjectInputStream(
      new FileInputStream(filename));// filename : 書き込み先ファイル
DataItem [] items = (DataItem[])in.readObject();
in.close();

次に書き込みはリスト[オブジェクト書き込み]のようになります。

オブジェクト書き込み
ObjectOutputStream out
    = new ObjectOutputStream(
      new FileOutputStream(filename));// filename : 読み込み元ファイル
out.writeObject(items);// item : DataItemの配列
out.flush();
out.close();

このようにjava.io.Serializableなオブジェクトであれば、このような単純な形式的なコードで何も考えずに書き込み、読み込みが出来るため、非常に手軽です。また、オブジェクトが複雑な構造を持っていたり、他のシリアライズ可能なオブジェクトへの参照を持っていても、そのままの構造で保存できてしまいます。

しかし欠点として、保存したデータがバイナリ形式であるため、アプリケーション以外から編集しにくいということが挙げられます。デバッグや運用時にアプリケーションにトラブルが起こった場合、問題の解決やデータの復旧のためには直接データを解析することが不可欠です。この方法はJavaの標準API方式で保存してあるため、データを直接解析することは全く不可能ではありませんが、非常に面倒です。

さらに、プログラムと強く結び付いているため、DataItemクラスを変更すると以前保存したデータを読み込めなくなる可能性があります。一応、そのような事態を回避するために、クラス変数serialVersionUIDを定義したり、readObjectwriteObjectメソッドを実装してバージョンの違いを吸収することも出来ますが、応急的な処置であり、変更に対して厳しいことには変わりありません。

以上の考察から、オブジェクト直列化による方法は、一時的な保存の用途に向いていると言えます。特にプロトタイプなどの開発の初期段階において、複雑なデータを手軽に保存する手段としては非常に有効です。しかし、バイナリ形式であることや、永久的なデータの読み書きが保証できないことから、本番用アプリケーションの保存形式としては採用しにくいと言えます。(1)

  1. ただし、Serializableは積極的に実装しておくと便利です。RMIによるオブジェクトの移動など、保存目的以外のオブジェクト直列化の応用もあります。

データベース

今までの例は、アプリケーションだけの機能で保存する方法でしたが、データの格納といえばやはりデータベースということで、外部のデータベースサーバーにJDBCで接続してデータを保存する方法があります。

データベースを使う利点は、特に大量のデータを扱う場合の高いパフォーマンスと信頼性があげられます。フリーのデータベースサーバーを使っても、数100万件程度のデータを問題なく扱うことができ、また表の組み合わせや検索などの高度な機能をSQLを用いて簡単に利用することが出来ます。また、障害復旧、同時アクセス管理、トランザクション等の本番の要求に十分耐える仕様を備えていることも大きな利点です。

しかし、利用にあたってはJavaプログラミング以外の高いスキルが必要です。具体的にはサーバーのインストールから始まり、起動、認証・アクセス管理、テーブル作成、JDBCドライバインストール、SQL、文字コードなどの数多くの手間とノウハウが必要です。このような手間を考えると、今回の目的のような手軽なデータ格納には利用することが出来ません。

ということで今回は詳しい比較は省略しますが、Relaxerがデータベースも守備範囲にあることから別の機会に詳しく述べたいと思います。

XMLと標準API

XMLの実体はテキストですが、構造を持ったデータを格納できるところがXMLの大きな特徴です。リスト[XMLデータ]がXMLを用いて表したデータです。

XMLデータ
<?xml version="1.0" encoding="Shift_JIS"?>
<dataList>
  <dataItem>
    <name>コーヒー</name>
    <cost>120</cost>
    <note>主食</note>
  </dataItem>
  <dataItem>
    <name>チョコレート</name>
    <cost>180</cost>
    <note>オヤツ</note>
  </dataItem>
  <dataItem>
    <name>ノート</name>
    <cost>150</cost>
    <note>いつでもどこでもアイデアを記録</note>
  </dataItem>
</dataList>

テキスト形式なのでデータを直接解析したり、編集することが可能です。また、複雑な構造を持つことが出来るので上記の保存形式にあった「複雑なデータが格納できない制限」は解消されます。さらに、プログラムから独立していますので「変更に弱い」という問題も解決されます。

さらに、XMLは規格がオープンであるため、パーサーや開発支援環境などの技術や情報が充実しており、習得や利用がしやすいという利点も見逃せません。

しかしながら、プログラミングには多少のコストがかかります。現在(2001/Nov)、XMLを操作する標準のAPIには、大きく分けてDOMとSAXという2種類の方法があるのですが、どちらも簡単なデータを読み書きするにも大きなコストがかかります。DOMとSAXによる読み込みプログラムは、付録の節[付録:DOMを用いた読み込み]節[付録:SAXを用いた読み込み]にまとめました。ちなみに、現在の標準API(DOM2)にはまだXMLを書き込むAPIはありませんが、次期API(DOM3)にて実現される予定です。

いくら拡張性があっても、このようにコストのかかるプログラムでは簡単なデータには利用する気が起きません。また、実際のアプリケーションにとっても、長いプログラムではバグが混入する可能性が高くなります。さらに面白くないことに、実現したい作業はルーチンワーク的なデータの解釈です。当然、プログラマ的には自動化を考えたくなるパターンであります。

XMLとRelaxer

XMLは非常に良い解決策なのですが、プログラミングの大変さが障害でした。

そこで、そのプログラミングの労力を乗り越えるツールがRelaxerです。Relaxerは、RELAXスキームと呼ばれる定義ファイルからデータの情報を読み取り、そのデータを読み込むためのプログラムを自動生成します。具体的にRelaxerを利用すると、読み込み・書き込みの部分はそれぞれリスト[オブジェクト読み込み]リスト[オブジェクト書き込み]になります。

オブジェクト読み込み
RDataList list = new RDataList(new File( filename ));
RDataItem [] items = list.getDataItem();
オブジェクト書き込み
//RDataList list

PrintWriter out = new PrintWriter
      (new OutputStreamWriter
	  (new FileOutputStream( filename ),"UTF-8"));
out.println(list.makeTextDocument());// 実質、保存はこの一行のみ
out.flush();
out.close();

このように、データの入出力がオブジェクト直列化と同程度の実質1行で終わります。つまり、RELAXスキームさえあれば、あらゆるファイルの読み書き部分のプログラムが終了してしまうわけです。

さて、そうすると後はRELAXスキームを作るだけですが、それほど難しくない文法ではあるのですが、やはり新しい言語を覚えるには多少の覚悟が要ります。

本来ならば、RELAXを覚えてエディタで編集するのが本来の姿なのですが、RELAXを覚えずに今すぐRelaxerを使うには現在以下の2つの選択肢があります。

DTD2RELAXは、DTDからRELAXスキームを生成するツールです。既にDTDが存在していたり、DTDに慣れ親しんでいる人にとっては重要なツールです。

MkRelaxは、RELAXスキームをGUIでビジュアルに編集するソフトです。GUIでスキームを設計できますので、新しくスキームを作るときはもちろん、他人の書いたスキームをグラフィカルに表示させてすぐに理解したい場面にも役に立つと思います。

今回はMkRelaxを用いてXML読み書きを実現するアプリケーションを開発します。