Javaについて徹底解説!

Java標準APIでのXML読み込み方4つを大紹介 サンプルで比較しよう

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

XML(Extensive Markup Language)は、プログラムの設定を書いたり、プログラム間でのデータ送信に使うデータフォーマットです。最近はJSONYAMLに押されてはいますが、それでも幅広く使われていますよね。

プログラムを作る時は、XMLを読む方法を知っていると便利です。多分、XMLを作るよりも読む機会の方がずっと多いでしょうし、XMLを読むのは簡単そうに見えて意外とてこずるものだったりするのです。

もちろんJavaではXMLの読み込みは標準機能としてあります。つまり、JavaをインストールすればXMLを読み込むためのライブラリが一緒に付いてきますので、使い方さえ少し学べば、今すぐにでも使い始められます!!

この記事では、JavaでのXMLの読み込み方を初心者向けにざっとお伝えします。大きく分けて4つのやり方があるのですが、それぞれのサンプルプログラムを通じて、使い方や特徴をご理解いただければと思います。

※この記事のサンプルは、Java 12の環境で動作確認しています

目次


1.Java SEの標準APIにはXMLを読み込む方法が4つある

JavaでXMLを読み込む方法は、Java SE 12の時点では以下の4つが代表的です。この記事では、これらの方法で同じXMLを読み込んでみて、それぞれの方法でプログラミングの仕方がどう違うのかを学んでいきます。

  • DOM(Document Object Model、ドム)
  • SAX(Simple API for XML Processing、サックス)
  • StAX(Streaming API for XML、スタックス)
  • XPath(XML Path Language、エックスパス)

なぜXMLを読み込むのに4つも方法があるの?と思われたかもしれません。なぜなら、それぞれのプログラムの作り方には、得意・不得意があるからです。逆に言えば、プログラムの都合に合わせて使い分けができるのです。

この4つ方法には、以下のような特徴があります。この記事のサンプルプログラムを順番にご覧になっていただけると、それらが具体的に分かると思います。

プログラミングの仕方 得意 不得意
DOM XMLのツリー構造を前提に読み込んだり検索する ツリー構造を使った要素の検索と操作 構造が入り組んだXMLの読み込み
SAX イベントベース 必要な要素の情報を簡単に得られる XMLのツリー構造を意識したプログラミング
StAX イベントベース(SAXとは少し異なる方法) 必要な要素の情報を簡単に得られる XMLのツリー構造を意識したプログラミング
XPath 専用の構文で必要な要素を指定する 読み取りたい要素や属性を一回のクエリで検索できる XPathだけでは何もできない(DOMの関連クラスの知識が必要)

1-1.この記事で使うXMLの紹介

この記事では、以下のXMLを共通して使います。内容は本(Book)の一覧(BookList)です。それぞれの本は、属性としてISBN(isbn)、著者名(author)、タイトル(title)があり、テキストとして本文の書き出しがあります。

1-2.この記事で目的とする出力内容

この記事では先ほどのXMLを読み込んで、本の情報を以下のように出力するプログラムを、それぞれの方法で作ります。同じXMLから同じことを出力するのですが、かなり違うやり方になりますよ。


2.DOM(Document Object Model)で読み込む

DOM(Document Object Model)は、HTMLなどの規格を作っているW3Cが決めたXMLの操作方法です。Javaに限らず、いろいろなプログラミング言語で共通で使える、XMLの読み込み方法です。

Document Object Model

https://ja.wikipedia.org/wiki/Document_Object_Model

 

W3C Document Object Model

https://www.w3.org/DOM/

 

Document Object Model (DOM) Technical Reports

https://www.w3.org/DOM/DOMTR

DOMの特徴は、XMLの「木構造(ツリー構造)」をプログラム上でそのまま扱えることです。XMLはルート要素に子要素がぶら下がるツリー構造を持ちますが、DOMではその構造ありきでプログラムをしていくのです。

サンプルXMLの構造

2-1.DOMでのXML読み込みプログラム

では、早速DOMを使ったJavaでのXMLの読み込みプログラムを見てみましょう。

2-2.プログラムの簡単な解説

このプログラムは以下のようなことをしています。

  • XMLからBookList要素を取得して…(4.)
  • BookList要素の下にあるBook要素を全て取得して…(5.)
  • それぞれのBook要素から属性とテキストを取得してprint!!(6. 7. 8.)

このプログラムで、サンプルXMLの何がどの変数に入っているのかを図にしました。上から順番に自分の子要素を得ているのがわかるでしょうか。これが、DOMXMLのツリー構造をベースにしているということです。

要素とDOMのクラスの対応

このように、DOMではXMLの一部分をDocumentからたどっていって必要なElementを探し出し、それらからテキストや属性値を取得したり、さらにその中のElementを検索してを続けていくのです。

DOMのインターフェイスの親子関係

2-3.【参考】DOMで要素を探す方法いろいろ

DOMで目的とする要素を探すには、いくつかのやり方があります。基本は以下のどれかになります。今回の例では2.を使いました。

  1. XMLのツリー構造を上から順にたどっていく(getChildNodesなどでたどって、要素名を確認する)
  2. getElementsByTagNameで、タグ名を指定して探す
  3. getElementByIdで、要素のIDを指定して探す
  4. DOMのTraversalの機能を使ってたどっていく(TreeWalker、NodeIterator、NodeFilterなど) ※ここではDOMのTraversalの詳細は省きます。

2-3-1.【重要】getElementByIdはID属性を検索する

XML文書へのgetElementByIdは少し注意が必要です。XMLの構造を決めるDTDXML Schemaで、ID属性として指定されている属性だけに有効です。決して、“id”“ID”という属性名を探しているわけではないのです。

例えば、以下のDTDで属性isbnの後ろに“ID”がありますよね。これがID属性の指定で、これによりisbn属性がBook要素のID属性として機能します。XMLDTDを紐付けていないとgetElementByIdでは検索できません。

同じことをXML Schemaで書けば以下のようになります。途中にtype=”xsd:ID”というものがありますが、これでisbn属性がID属性であることを指定しているのです。


3.SAX(Simple API for XML Processing)で読み込む

SAX(Simple API for XML Processing)はXMLを読み込むためのAPIです。SAXの特徴は、イベント駆動と呼ばれるプログラムの仕方になることです。

SAX

http://www.saxproject.org/

 

javax.xml.stream (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/package-summary.html

 

javax.xml.stream.events (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/events/package-summary.html

 

javax.xml.stream.util (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/util/package-summary.html

イベント駆動のイメージは、XMLを先頭から順番に読んでいった時の出来事、つまり要素が出現した、要素が終わった、テキストが出現したなどの「イベント」ごとに、あらかじめ決めてある処理が呼ばれるものです。

3-1.SAXでのXML読み込みプログラム

これも百聞は一見に如かず!! まずはSAXXMLを読み込むプログラムをご覧ください。

3-2.プログラムの簡単な解説

さて、DOMとはずいぶんプログラムが変わりました。Book要素を探していませんし、ループもありません。でも、これを実行するとDOMの場合と同じ出力になるのです不思議ですね。

プログラムの考え方は、以下となります。先ほどお伝えしたとおり、XMLを先頭から読み取っていって、要素やテキストが出現するごとに、DefaultHandlerが持つメソッドのうち、決められたものが呼ばれるのです。

XMLとSAXのイベント発生タイミング

ですから、DOMとは違いXMLの構造を意識せずにすむので、プログラムは比較的簡単です。SAX“Simple”はこれを意味しています。処理したい要素に到着するまで待って、到着したらやりたいことをするのです。

逆に、XMLの構造を強く意識したプログラムは、SAXは苦手です。今処理している要素の親要素や子要素が何かが、SAXでは直接わからないからです。ですので、ツリー構造を意識したプログラムは複雑になります。

3-3.【参考】SAXでのイベントの種類

SAXのイベントの発生タイミングをまとめました。詳しくは以下のJavadocか、SAX Projectのドキュメントを参照してください。DefaultHandlerは~Handlerを全て空で実装している、継承元として便利なクラスです。

 

ContentHandler (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/org/xml/sax/ContentHandler.html

 

DTDHandler (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/org/xml/sax/DTDHandler.html

 

EntityResolver (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/org/xml/sax/EntityResolver.html

 

ErrorHandler (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/org/xml/sax/ErrorHandler.html

 

DefaultHandler (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/org/xml/sax/helpers/DefaultHandler.html

ContentHandler

  • startDocument:XML文書が始まった時
  • endDocument:XML文書が終わった時
  • startElement:要素が始まった時
  • endElement:要素が終わった時
  • startPrefixMapping:名前空間のマッピングが始まった時
  • endPrefixMapping​:名前空間のマッピングが終わった時
  • characters:文字が出現した時
  • ignorableWhitespace:無視できる空白文字が出現した時
  • processingInstruction:処理命令(<? ~ ?>)が出現した時
  • skippedEntity:処理に失敗したエンティティがあった時
  • setDocumentLocator:読み取っているXMLの桁数・行数が変わった時

DTDHandler

  • notationDecl:表記法宣言(<!NOTATION ~>)が出現した時
  • unparsedEntityDecl:解析対象外エンティティ宣言が出現した時

EntityResolver

  • resolveEntity:外部エンティティの解決が必要な時

ErrorHandler

  • warning:XMLの処理中に警告が発生した時
  • error​:XMLの処理中にエラーが発生した時
  • fatalError:XMLの処理中に復帰不可能なエラーが発生した時

4.StAX(Streaming API for XML)で読み込む

次はStAX(Streaming API for XML)です。これもDOMやSAXとは違うスタイルのプログラミングになります。StAXはイベント駆動なプログラミングスタイルの一種ですが、SAXとはイベントの取り扱い方が違います。

Streaming API for XML – ウィキペディア

https://ja.wikipedia.org/wiki/Streaming_API_for_XML

 

JSR 173 – JCP.org

https://jcp.org/en/jsr/detail?id=173

StAXの特徴は以下のものです。

  • Pull型である。イベントの種類をStAXのクラスに聞いて、必要な処理を自分で呼び出す。
  • Iteratorパターンで実装する。Iterateする要素は、XMLを読む上で起きたイベントである。
  • SAXよりも制限が緩い。SAXのように決まったクラス(DefaultHandler)やインターフェイス(ContentHandlerなど)を継承・実装しなくてもいい。

なお、StAXではXMLの読み込みにXMLStreamReaderXMLEventReaderのどちらかを使います。これらの間でも、少しプログラミングの仕方が違うので、ここでは両方紹介します。

4-1.StAXでのXML読み込みプログラム(XMLStreamReaderを使う方法)

StAXのXMLStreamReaderXMLを読み込むプログラムは、こんな感じになります。

4-2.プログラムの簡単な解説(XMLStreamReader)

全体の雰囲気はSAXに似ています。でも、StAXでは起きたイベントが何かを自分で調べて、対応する処理を自分で書きます。一方、SAXでは起きたイベントごとに、イベントハンドラのメソッドが自動で呼び出されます。

XMLとStAXのイベント発生タイミング

今、XMLのどこにいるかはXMLStreamReader自身が分かっています。まだ次のイベントがあるならhasNexttrueを戻し、nextするとどのイベントかをXMLStreamConstantsにある数字で戻してくれます。

 XMLStreamConstants (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/XMLStreamConstants.html

そして、XMLStreamReaderのメソッドを呼べばXMLの情報を得られます。今いるのが要素ならタグ名が分かりますし、属性ならその値、テキストならその文字列などをXMLStreamReaderが教えてくれます。

XMLStreamReader (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/XMLStreamReader.html

4-3.StAXでのXML読み込みプログラム(XMLEventReaderを使う方法)

StAXのもう一つのクラスXMLEventReaderXMLを読み込むプログラムは、こんな感じになります。

4-4.プログラムの簡単な解説(XMLEventReader)

プログラムの流れはXMLEventReaderでも、XMLStreamReaderと同じですというか、わざと同じにしました。XMLStreamReaderの例ではswitch文で、こちらはif文です。でも、それは本質的な違いではないのです。

XMLEventReaderでは、イベントがXMLEventにカプセル化されます。これがXMLStreamReaderとの違いです。XMLEventを使えば、イベントへの処理を別のメソッドやクラスに行わせるのが簡単・安全になるのです。

XMLEventReader (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/XMLEventReader.html

 

XMLEvent (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/stream/events/XMLEvent.html

なぜかというと、XMLEventには次のイベントに進めるメソッドがないので、他のクラスやメソッドにXMLEventを渡しても、XMLの読み取りを安全に進められます。さらに、イベントハンドラを自分でも作れるのです。

XMLStreamReaderはhasNextnextができるので、XMLStreamReaderを別のクラスやメソッドに渡すと、こっそり次のイベントへ進められます。それが役に立つこともありますが、バグの原因にもなりかねません。

4-4-1.【発展】XMLEventを使ったイベントハンドラの例

SAXでのContentHandlerのようなクラスをXMLEventで簡単に作ってみると、以下となります。こういうものを自分の都合に合わせて自由に作れるのが、XMLEventReaderXMLEventのいいところです。


5.XPath(XML Path Language)で読み込む

XPathは他のAPIとは大分違います。XPathはXMLの検索方法の一つで、データベースへのSQLに似ています。検索条件をXPathの構文で書いてXMLに問い合わせると、条件を満たす要素やテキストが簡単に得られます。

XML Path Language

https://ja.wikipedia.org/wiki/XML_Path_Language

 

XML Path Language (XPath)

https://www.w3.org/TR/1999/REC-xpath-19991116/

 

javax.xml.xpath (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.xml/javax/xml/xpath/package-summary.html

DOMでは要素を順番にたどったり、タグ名やID属性での検索もできます。でも、プログラムの行数は増えてしまいがちです。SAXStAXはイベント駆動なので、要素の親子関係を意識した処理はちょっと苦手です。

でも、XPathではそれらをすべてクリアできます。欲しい情報をダイレクトに、かつ短いプログラム行数で得られます。要素の親子関係で検索できますし、属性値が何か、テキストに何かを含む、なども指定できるのです!!

5-1.XPathでのXML読み込みプログラム

ではさっそくXPathXMLを読み込んでみましょう。

5-2.プログラムの簡単な解説

XPathは検索の仕組みなので、XPathでは検索条件を指定するだけで、検索結果はDOMのクラスになります。ここでは検索結果はNodeListで戻すように、検索をする時の引数で指定しています。

キモは、3.でのXPathでの検索条件です。文字列で“/BookList/Book”とありますよね。これは「ルート要素のBookList要素の直下にあるBook要素を全て取得する」を意味しているのです。

4.でXMLから検索すると、XPathでの検索条件にヒットしたモノが戻ります。ですから、DOMでの検索やSAXStAXのプログラムの作り方とはかなり違いますよね。これはSQLで検索して結果を得るのと似ています。

5-3.【参考】XPathでのいろいろな検索の仕方

先程の例では要素のタグ名で検索しました。もちろんXPathではもっと別の検索条件も使えます。属性の値、テキストの部分一致などです。検索結果としても、要素だけではなく属性の値や、テキストを指定できます。

例えば、以下のようにもできるのです。欲しいものをXPathの検索条件として指定すれば、それが得られます。なんとなく雰囲気が分かるかと思います。多分、[]が抽出条件で、@が属性かなぁという感じがしますよね。

例1: ISBNが”ISBN978-4-1234-0001-5″のBook要素を取得する

 

例2: テキストに”cat”を含むBook要素を取得する

 

例3: title属性が”The Cats of Ulthar”であるBook要素のauthor属性の値を取得する

 

例4: title属性が”The Cats of Ulthar”であるBook要素のテキストを取得する

XPathで出来ることは、実はまだまだこんなものではありません。もし興味があれば、XPathをもっと深く学んでみるといいでしょう。そして、XPathが分かれば、その関連仕様であるXQueryの理解も見えてきますよ。


6.【参考】JAXB(Java Architecture for XML Binding)で読み込む

過去のJava SEでは、JAXB(Java API for XML Binding)というAPIも使えました。このAPIは今もありますが、Java SE 11からは削除され、エンタープライズ向けのJava EE(Jakarta EE)の機能の位置付けになりました。

JAXBの特徴は、XMLの構造とJavaのクラスを直接紐付けることです。XMLと紐付けたクラスを作っておけば、JAXBのクラスのメソッドを呼び出すだけで、XMLの内容が反映されたクラスのインスタンスが得られます。

つまり、DOMSAXStAXのように、XMLをプログラム上であれこれいじり回す必要はないのです。その代りに、アノテーションなどでXMLの構造とクラスを紐付けておくという、事前作業が必要になります。

6-1.JAXBでのXML読み込みプログラム

この例のXMLでは、BookListBookという二つの要素があります。ですので、今回のJAXBの例では対応する二つのクラスをXMLの構造に合わせて作ってみます。

6-2.プログラムの簡単な解説

BookListは、フィールドとしてBookListであるbooksを持っているだけのクラスです。booksへ、XmlElementでこのListの内容をBookという要素として出力するように指定しています。

Bookでは、各フィールドを要素の属性として出力するよう、XmlAttributeで指定しています。属性名はフィールドの変数名が使われます。さらに、textを要素のテキストとして出力するようXmlValueで指定しています。

そして、JAXB.unmarshalの引数にXMLとクラスを指定すれば、Javaのクラスのインスタンスにできます。なお、この例ではXMLから読んでいますが、逆にインスタンスをXMLにすることも、以下のようにできるのです。

6-3.Java SE 9以降でJAXBを使う方法

Java SE 9/10ではJAXB関連のクラスがあるモジュールはまだありますので、JAXBのモジュールをaddすれば使えます。例えば、javaコマンドへの引数に –add-modules java.xml.bind あるいは –add-modules java.se.ee を追加すれば一応は使えます。

ですが、Java SE 11からはJAXB関連のクラスがあるモジュール自体が含まれなくなってしまいましたので、このaddする方法はもう使えません。どこかから、JAXBで使うクラスを持ってこなければならないのです。

JAXBを使うためのAPIや実行クラスは、例えばMavenを使っている場合は、pom.xmlへ以下の依存先ライブラリを追加することで使えるようになります(バージョンは2019/4時点での最新のものです)


7.【参考】普通のファイル読み込み+文字列処理で読み込む

今までご紹介してきたXML用のAPIを使わなくても、XMLを読み込むことはできます。XMLと言えども単なる文字列の集まりですから、できることを割り切ってしまえば、文字列処理の範囲で対応できなくもないからです。

XMLの構造が分かっていて、かつ書かれ方が統一されていれば、以下のようにも作れます。ある意味で、処理内容はとても分かりやすいですし、XMLの解析は文字列処理の勉強をするのにちょうどいいテーマでもあります。

7-1.普通のファイル読み込み+文字列処理でのXML読み込みプログラム

ここでは、普通のファイル読み込みと正規表現を使ったプログラムの簡単な例をご紹介します。XMLの仕様からしてみれば機能が足りないところだらけですが、実用上ではこんなものでも十分使えるものだったりします。

7-2.プログラムの簡単な解説

このプログラムでは、XMLファイルの行をすべて読み込んで、行単位に処理をしています。行の文字列をsubstringしても別にいいのですが、正規表現を使う方がプログラミングが楽ですし、短くもなります。

まず、Book要素の行かどうかを、属性部分とテキスト部分を抜き出す正規表現で確認しています。マッチさせるのと同時に、マッチした部分を後から抜き出せるよう、マッチ部分に名前を付けています(attrtext)

さらに、Book要素だと思われる行なら、出力したい属性を正規表現で抜き出します。こちらも属性名と属性値のマッチ部分に名前を付けて(attrnameattrvalue)、マッチした部分すべてをループして取り出しています。

正規表現でのポイントは、マッチ部分に名前を付けていること(?<名前>)と、マッチ方法を最短一致(.+?)としていることです。Javaの正規表現は通常は最長一致なので、余計なものまでついてくるからですね。


8.まとめ

この記事では、Java SEで使えるXML読み込みの4つの方法を、サンプルを交えてお伝えしました。その4つの方法とは、DOMSAXStAXXPathです。その他にも、JAXBXMLを直接解釈する方法もご紹介しました。

同じXMLを読んで同じ出力をするだけでも、やり方が大きく違うとお分かりいただけたかと思います。ですので、プログラムでやりたいことにピッタリのやり方を選べるよう、それぞれ使い方を学んでおくといいでしょう。

また、Javaの標準APIの他にも、以下のような外部ライブラリが使えます。DOMSAXStAXXPathなどのいいとこ取りをしているようなものもありますので、興味があれば使ってみてもいいと思いますよ。

『技術力』と『人間力』を高め定年まで働けるエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって、エンジニアとしてだけでなくビジネスパーソンとして高い水準を目指し、社会や顧客に必要とされることで、関わる人々に感動を与える集団であろうと思っています。

  • 定年までIT業界で働くためのスキルが身につく「感動大学」と「技術勉強会」!
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な「人事評価制度」!
  • 理想のエンジニア像に近づくためのよきアドバイザー「専任コーチ制度」!
  • 稼動確認の徹底により実現できる平均残業時間17時間の働きやすい環境!

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。