書くことが思いつかず前の記事から2週間以上空いてしまいました。
前々回まで、StreamAPIの練習として適当に作成していた図書館の貸し出しをモデルにしたプロジェクトを書いていましたが、今回はStreamAPIとは別方向の改修を行いたいと思います。
1.前回までのコード
public class BookList {
Map<String, Book> bookList = new HashMap<>();
public void setBookList() {
bookList.put("こころ", new Book("こころ", "夏目漱石", 200));
bookList.put("斜陽",new Book("斜陽", "太宰治", 180));
bookList.put("想像の共同体",new Book("想像の共同体", "アンダーソン", 350));
bookList.put("近代文学論争",new Book("近代文学論争", "臼井吉見", 300));
bookList.put("近代天皇像の形成",new Book("近代天皇像の形成", "安丸良夫", 400));
bookList.put("天皇の肖像",new Book("天皇の肖像", "多木浩二", 190));
bookList.put("ドストエフスキーの詩学",new Book("ドストエフスキーの詩学", "バフチン", 500));
bookList.put("『キング』の時代",new Book("『キングの時代』", "佐藤卓己", 530));
}
面倒なのでDBは使わない方針でしたが、そのため、書籍一覧を保持するBookListクラスのオブジェクトは以上のように初期化されていました。
上記のコードでbookListが保持しているBookクラスのオブジェクトには、貸出履歴や貸出回数を管理するメンバ変数が存在しますが、当然のことながら、JVM(Java Virtual Machine)が終了した時点でそれらの変数は解放されてしまいます。
そこで今回は、DBを使わずにXMLファイルを読み込むことでBookListの初期化を行いたいと思います。
XMLファイルを読み込む練習がしたいだけなので、DB使えよ…みたいな指摘はなしで。
2.XMLファイルとは
.xmlファイルとは、ファイル名の拡張子(末尾部分)が「.xml」のファイルで、XML(Extensible Markup Language)で記述されたテキストファイルのこと。ソフトウェアの設定ファイルなどによく見られる。
IT用語辞典 e-Words(https://e-words.jp/w/.xml%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.html)
xmlファイルでは、自分で任意のタグを用意することでデータを定義することができます。
例えば今回改修範囲であるBookクラス(書籍名・著者・ページ数などを保持するクラス)をXMLで表現すると以下のようになります。
<books>
<book id="0001">
<title>こころ</title>
<author>夏目漱石</author>
<year>1914</year>
<lentCount>0</lentCount>
<lastLent></lastLent>
<pages>250</pages>
</book>
<book id="0002">
<title>門</title>
<author>夏目漱石</author>
<year>1910</year>
<lentCount>0</lentCount>
<lastLent></lastLent>
<pages>270</pages>
</book>
3.JAXBの利用
さて、XML形式で外出しした書籍一覧情報ですが、これを実際のコード内で活用するには、
- XML形式の構文解析
- 毎にBookクラスのオブジェクトに変換
という手順を踏む必要があります。
こういった場合に使用できるAPIに、JAXB(Java Architecture XML Binding)があります。
JAXB(Java Architecture XML Binding)は、[…]XMLファイルを読み込むというよりも、「XMLファイルとJavaのオブジェクトを結び付ける」動作をします。JAXBを利用することで、XMLとJavaオブジェクトを相互変換できます。
Java本格入門p.262
JAXBはかつてはJavaの標準ライブラリ(Eclipseのパッケージエクスプローラ内のJRE システムライブラリーに格納されているパッケージ)であったが、現在は削除されているため、自力で導入する必要があります。導入については割愛。
【BookList.java】
private Books makeBookList() {
try(InputStream is = Files.newInputStream(Paths.get("./src/main/resources/xml/Book.xml"))){
bookList = JAXB.unmarshal(is, Books.class);
}catch(IOException e) {
System.err.println(e);
}
return bookList;
}
JAXBクラスのunmarshalメソッド(XMLからJavaオブジェクトへの変換を行うメソッド)を使用しています。引数にInputStreamを渡していますが、InputStreamの代わりにReaderクラスオブジェクトが指定されているオーバーロードもあるので、BufferedReaderでも大丈夫です。
第二引数にはBooksクラスのクラスリテラル(そのクラスを表すClassクラスのオブジェクト)が指定されます。
では、肝心のBooksクラスはどうなっているかというと、
【Books.java】
@XmlRootElement(name = "books")
public class Books {
private List<Book> bookList;
@XmlElement(name = "book")
public List<Book> getBookList(){
return bookList;
}
public void setBookList(List<Book> bookList) {
this.bookList = bookList;
}
}
@XmlRootElementは、オブジェクトをXML要素として表現するための注釈で、XMLのルートタグ(最上位の要素)と、ツリー構造で表現されるJavaのオブジェクトの、最上位のクラスを結び付けます。ここでは、Bookオブジェクトを格納するArrayListではなく、クラスに注釈を付与している点に注意しておきます。
@XmlElementも同様に、XML要素とオブジェクトを結び付ける注釈です。
このように、注釈型を用いるだけで、XML要素がJavaオブジェクトにマッピング(関連づけ・割り当て)されます。
4.実際に動かす

先掲のBookList.java内のbookListをwatch式で見てみます。
個人的には、@XmlRootElement注釈はBooksクラスに付与したのに、Booksクラスのメンバ変数であるbookListにバインドされているのが親切なような不気味なような…。
ともかく、XMLの各要素がBook型のリストに格納され、各BookオブジェクトはXMLタグで囲われた値を保持していることがわかります。ただし、
<book id="0001">
<title>こころ</title>
<author>夏目漱石</author>
<year>1914</year>
<lentCount>0</lentCount>
<lastLent></lastLent>
<pages>250</pages>
</book>
<book>タグの属性であるidは、Bookクラスのメンバ変数であるidにはバインドされていません。
タグ内の属性をバインドするには、@XmlAttribute注釈型を用います。
@XmlAttribute(name = "id")
public int getId() {
return id;
}
getterに@XmlAttributeを付与していることに注意します。メンバ変数に直接付与してもバインドされません。注意しましょう。

きちんとidもバインドされています。もっとも、XMLで"0001"だったidはint型の1として扱われ、0埋めではなくなっていますが…。