JAXB その3
来源:互联网 发布:小公司网络需求调查表 编辑:程序博客网 时间:2024/06/01 10:33
2週に渡って,JAXBを使用したアンマーシャリング/マーシャリングを解説してきました。とはいうものの,そこで扱ったXMLドキュメントは,すべてファイルでした。
そこで,今週はファイルではない対象を扱ってみましょう。
取りあげるのはStAXとDOMです。もちろん,StAXもDOMもXMLパーサなので,単独でXMLドキュメントを解釈することが可能です。
では,なぜ複数のパースを組み合わせる必要があるのでしょうか。
たとえば,長大なXMLドキュメントの一部しか必要がない場合はどうでしょう。SAXやStAXで必要なところまで読み飛ばし,必要なところだけJAXBでアンマーシャリングします。もちろん,そのままSAXやStAXでパースしてもかまいませんが,スキーマがある場合はJAXBが簡単です。
また,DOMとXPathを組み合わせれば,必要な部分をクエリーすることが簡単にできます。必要な部分が見つかれば,後はJAXBにまかせてしまうというわけです。
このように用途によっては,パースの手段を組み合わせるということも十分意味を持ちます。
では,さっそくJAXBと他のパースを組み合わせてみましょう。
その前に,今週使用するXMLドキュメントを決めておきます。今週は,CDなどの音楽アルバムを表すXMLドキュメントを使用することにしました。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><album xmlns="http://www.javainthebox.net/music"> <title>Deja Vu</title> <credit>Crosby, Stills, Nash & Young</credit> <songs> <song title="Carry On" writer="Stephen Stills" /> <song title="Tech Your Children" writer="Graham Nash" /> <song title="Almost Cut My Hair" writer="David Crosby" /> <song title="Helpless" writer="Neil Young" /> <song title="Woodstock" writer="Joni Mitchell" /> <song title="Deja Vu" writer="David Crosby" /> <song title="Our House" writer="Graham Nash" /> <song title="4 + 20" writer="Stephen Stills" /> <song title="Country Girl" writer="Neil Young" /> <song title="Everybody I Love You" writer="Stephen Stills, Neil Young" /> </songs></album>
ルートタグが<album>タグで,その中に<title>タグと<credit>タグがあります。そして,曲のデータをまとめる<songs>タグがあります。個々の曲は<song>タグで表されます。
このXMLドキュメントのスキーマをXML Schemaで表すと次のようになります。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><xs:schema version="1.0" targetNamespace="http://www.javainthebox.net/music" xmlns:tns="http://www.javainthebox.net/music" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="album"> <xs:complexType> <xs:sequence> <xs:element ref="tns:title" minOccurs="1"/> <xs:element ref="tns:credit" minOccurs="1"/> <xs:element ref="tns:songs" minOccurs="1"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="title" type="xs:string"/> <xs:element name="credit" type="xs:string"/> <xs:element name="songs"> <xs:complexType> <xs:sequence> <xs:element ref="tns:song" minOccurs="1" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="song"> <xs:complexType> <xs:attribute name="title" type="xs:string"/> <xs:attribute name="writer" type="xs:string"/> </xs:complexType> </xs:element> </xs:schema>
このようなXMLドキュメントで,JAXBで扱うのは<song>タグとしてみましょう。
他のパースと組み合わせる前に,このスキーマからJavaのクラスを生成しておきます。
C:\jaxb>xjc album.xsdnet\javainthebox\music\Album.javanet\javainthebox\music\ObjectFactory.javanet\javainthebox\music\Song.javanet\javainthebox\music\Songs.javanet\javainthebox\music\package-info.java
これで準備は整いました。
StAXとJAXBの組み合わせ
まず,StAXとJAXBを組み合わせてみます。
StAXで<song>タグまでパースし,その後JAXBに引き渡します。
まず,StAXUnmarshallerSampleクラスのフィールドとコンストラクタを示します。
public class StAXUnmarshallerSample { // StAX 用ファクトリ private XMLInputFactory factory; // JAXB 用ファクトリ private JAXBContext context; // JAXB アンマーシャラー private Unmarshaller unmarshaller; public StAXUnmarshallerSample() throws JAXBException { // StAX用ファクトリの生成 factory = XMLInputFactory.newInstance(); // JAXB用ファクトリの生成 context = JAXBContext.newInstance("net.javainthebox.music"); // アンマーシャラー生成 unmarshaller = context.createUnmarshaller(); }
StAXもJAXBも,ファクトリクラスはパースやアンマーシャリング/マーシャリングのたびに生成するのではなく,なるべくまとめて生成するようにします。そのために,ここではコンストラクタで一度だけ生成するようにしました。
次にStAXでのパースをおこなうparseメソッドです。mainメソッドからは,このparseメソッドがコールされます。
// StAX でパース public void parse(String xmlfile) { XMLStreamReader reader = null; InputStream stream = null; try { stream = new FileInputStream(xmlfile); // パーサの生成 reader = factory.createXMLStreamReader(stream); // イベントループ while (reader.hasNext()) { // 次のイベントを取得 int eventType = reader.next(); if (eventType == XMLStreamReader.START_ELEMENT) { // タグ名がsongかどうか調べる if ("song".equals(reader.getLocalName())) { // song タグの場合JAXBでアンマーシャル unmarshal(reader); } } } } catch (FileNotFoundException ex) { System.err.println("ファイルがオープンできません"); } catch (XMLStreamException ex) { System.err.println("パースに失敗しました"); } finally { if (reader != null) { try { reader.close(); } catch (XMLStreamException ex) {} } if (stream != null) { try { stream.close(); } catch (IOException ex) {} } } }
ここではStAXのカーソルAPIを使用してパースをおこなっています。StAXの詳細はJava SE 6完全攻略 第69回 第三のパーサ - StAXをご参照ください。
カーソルAPIでは,XMLStreamReaderオブジェクトからイベントを取り出し,そのイベントに応じた処理を行ないます。
ここではSTART_ELEMENTイベントだけを解釈しています。この時,青字で示したように,タグ名がsongであるかを調べています。
タグ名がsongの場合はunmarshalメソッドをコールし,JAXBでアンマーシャリングします。赤字で示したように,引数はXMLStreamReaderオブジェクトです。
// JAXB でアンマーシャリング private void unmarshal(XMLStreamReader reader) { try { // アンマーシャリング // 戻り値の型はObjectクラス Object obj = unmarshaller.unmarshal(reader); Song song = (Song)obj; // song タグの内容を出力 System.out.println(song.getTitle() + " (" + song.getWriter() + ")"); } catch (JAXBException ex) { // 例外処理 System.err.println("アンマーシャリングに失敗しました"); } }
unmarshalメソッドの引数は,XMLStreamReaderオブジェクトです。アンマーシャリングの対象がストリームの場合と違うのは,この引数だけです。
ただしくアンマーシャリングできれば,unmarshalメソッドの戻り値は,XMLドキュメントに対応するオブジェクトになります。
ここでは,StAXで<song>タグまでパースしたので,JAXBでアンマーシャリングを開始するのは<song>タグからとなります。このため,unmarshalメソッドの戻り値の型はSongクラスになります。
しかし,unmarshalメソッドの戻り値の型はObjectクラスと定義されているため,Songクラスにキャストします。
もし,このキャストを排除したい場合は,以下に示すように,引数がXMLStreamReaderインタフェースと,Classクラスにオーバーロードされているunmarshalメソッドを使用します。この場合,戻り値はジェネリクスを用い,JAXBElement<T>となります。Tは第2引数で指定されたクラスとなります。
JAXBElement<Song> element = unmarshaller.unmarshal(reader, Song.class); Song song = element.getValue();
では,実行してみましょう。使用するXMLドキュメントは一番始めに示したXMLドキュメントを使用しました。
C:\jaxb>java StAXUnmarshallerSample dejavu.xmlCarry On (Stephen Stills)Tech Your Children (Graham Nash)Almost Cut My Hair (David Crosby)Helpless (Neil Young)Woodstock (Joni Mitchell)Deja Vu (David Crosby)Our House (Graham Nash)4 + 20 (Stephen Stills)Country Girl (Neil Young)Everybody I Love You (Stephen Stills, Neil Young)
曲名と,カッコ内に作曲者が表示されていることがお分かりのはずです。
DOMとJAXBの組み合わせ
次に,DOMとJAXBの組み合わせです。
このサンプルも先ほどのサンプルと同様,<song>タグだけをJAXBでアンマーシャリングします。ここで,<song>タグを抜き出すためにXPathを使用します。
まず,DOMUnmarshallerSampleクラスのコンストラクタを示します。
public DOMUnmarshallerSample() throws ParserConfigurationException, JAXBException { // DOM の初期化 factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); builder = factory.newDocumentBuilder(); // XPath の初期化 xpathFactory = XPathFactory.newInstance(); xpath = xpathFactory.newXPath(); // JAXB の初期化 context = JAXBContext.newInstance("net.javainthebox.music"); unmarshaller = context.createUnmarshaller(); }
コンストラクタでは,DOMのDocumentBuilderオブジェクト,XPathのXPathオブジェクト,JAXBのUmarshallerオブジェクトを生成しています。
DocumentBuilderFactoryクラスのsetNamespaceAwareメソッドを引数trueでコールしているのは,DOMで名前空間を認識させるためです(赤字部分)。
つづいて,DOMによるバース処理です。
// DOM でパース public void parse(String xmlfile) { try { // パース Document document = builder.parse(new File(xmlfile)); // 名前空間の処理 NamespaceContext context = new NamespaceContext() { public String getNamespaceURI(String prefix) { if ("pre".equals(prefix)) { return "http://www.javainthebox.net/music"; } else { return XMLConstants.NULL_NS_URI; } } public String getPrefix(String uri) { throw new UnsupportedOperationException(); } public Iterator getPrefixes(String uri) { throw new UnsupportedOperationException(); } }; xpath.setNamespaceContext(context); // XPath でクエリ NodeList list = (NodeList)xpath.evaluate("/pre:album/pre:songs/pre:song", document, XPathConstants.NODESET); // クエリ結果のノードを JAXB でアンマーシャリング for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); unmarshal(node); } } catch (IOException ex) { System.err.println("パースに失敗しました"); } catch (SAXException ex) { System.err.println("パースに失敗しました"); } catch (XPathExpressionException ex) { System.err.println("パースに失敗しました"); } }
NamespaceContextインタフェースを実装した無名クラスを作成しているのは,XPathで名前空間を扱うためです。青字で示した,getNamespaceURIメソッドでプリフィックスと名前空間の対応を決めます。
そして,XPathでクエリーを行なっているのがXPathクラスのevaluateメソッドです(赤字部分)。ここで,getNamespaceURIメソッドで対応付けしたプリフィックスを使用して,ノードのクエリを行ないます。
evaluateメソッドの第3引数でNODESETを指定しているので,戻り値はNodeListオブジェクトになります。後はNodeListオブジェクトが保持するNodeオブジェクトをJAXBでアンマーシャリングします。
// JAXB でアンマーシャリング private void unmarshal(Node node) { try { // アンマーシャリング Object obj = unmarshaller.unmarshal(node); Song song = (Song)obj; // song タグの内容を出力 System.out.println(song.getTitle() + " (" + song.getWriter() + ")"); } catch (JAXBException ex) { // 例外処理 System.err.prntln("アンマーシャリングに失敗しました"); } }
Nodeオブジェクトを使用した場合,unmarshalメソッドの引数が変化するだけで,後は何も変りません。他の場合と,同じように処理を行なうことができます。
実行した結果はStAXの場合とまったく一緒なので,ここでは省略します。
ここで示したように,JAXBは他のパースと組み合わせて使うことも簡単にできます。XMLのパースにはそれぞれ得手,不得手があります。特性に応じた使い分けをすることで効率的にXMLドキュメントを扱うことができるはずです。
さて,今までの解説でははじめにスキーマがあることを前提に考えていました。来週はそれとは逆に,JavaのクラスからXMLのスキーマを生成することを考えていきます。お楽しみに。
- JAXB その3
- JAXB その1
- JAXB その2
- JAXB その4
- JAXB その5
- JAXB その6
- JAXB
- jaxb
- JAXB
- JAXB
- jaxb
- JAXB
- JAXB
- JAXB
- JAXB
- JAXB
- JAXB
- JAXB
- window的方法
- tensorflow实例(2)--机器学习初试
- Halide学习笔记----Halide tutorial源码阅读7
- POJ 2975 Nim题解---简单的博弈论
- Inten对象中的Flag
- JAXB その3
- JVM最大线程数
- Java基础语法篇(3)
- Django框架学习笔记(1.安装创建初识)
- java 基本数据类型及自动类型提升
- Ripple Labs 为什么能获得 Google Ventures、IDG 资本等风投机构青睐
- android技巧篇
- 单反入门拍摄
- DJANGO 2.0变化