第5回 2007年5月25日 応用Java (Java/XML)
前回までやったこと 「XMLパーサ」-- Java でXMLを処理 javax.xml.stream パッケージのパーサ イベントの種類の調べる 空白文字のチェック 属性の処理 応用(1) – テーブルの利用 「要素の出現回数を調べる」 java.util.Map, java.util.Set
本日(5/25)の講義内容 javax.xml.stream の処理の応用(2) フィルタ的な処理 MS Office 2007 のXMLを解析する java.util.Collection のフレームワーク Set, HashSet, SortedSet Map, HashMap, TreeMap
(復習) パッケージとAPI javax.xml.stream XMLEventReader インタフェース XMLInputFactory クラス javax.xml.stream.events XMLEvent インタフェース サブインタフェース群 StartDocument, EndDocument, StartElement, EndElement, Characters
javax.xml.stream のAPI(1) XMLEventReader インタフェース 構文解析を実行(つまりパーサ) java.util.Iterator として扱える =発見した要素などの構成要素の集まり (つまり、処理結果のデータでもある) hasNext()メソッド、 next()メソッド nextEvent()メソッド <doc><title>ABC</title></doc>
javax.xml.stream のAPI(2) XMLInputFactory クラス XMLEventReader のインスタンスを得るための専用のクラス =それ自身は処理を実行するわけではない 会社の「受付係」、「案内係」の役割
Factory の「デザインパターン」 XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader reader = factory.createXMLEventReader( source ); Factoryクラスはまず自分のインスタンスを作る Factoryのインスタンスから実際に処理を行うインスタンスを得る
必要なデータの取り出し方 XMLEventReader reader = …..; while( reader.hasNext() ) { XMLEvent event = reader.nextEvent(); System.out.println( event ); } <doc><title>ABC</title></doc>
XMLEvent とその仲間たち XMLEventReader がかかえるデータ javax.xml.stream.events パッケージが提供 XMLEvent が共通のスーパインタフェース StartDocument, EndDocument StartElement, EndElement Characters, Comment Attribute など <doc><title>ABC</title></doc>
プログラムの基本部分(1) import javax.xml.stream.events.*; XMLEventReaderTest1.java import javax.xml.stream.*; import javax.xml.stream.events.*; import javax.xml.transform.stream.*; public class XMLEventReaderTest1 { public static void main( String[] args ) {
プログラムの基本部分(2) try { StreamSource source = new StreamSource( args[0] ); XMLInputFactory factory = XMLInputFactory.newInstance(); XMLEventReader reader = factory.createXMLEventReader( source );
プログラムの基本部分(3) int count=0; while( reader.hasNext() ) { XMLEvent event = reader.nextEvent(); System.out.println( "Event " + count + ":" + event ); count++; }
処理対象のXML文書の例 sample1.xml <document> <title>Java and XML</title> Let's begin, now! </document>
実行結果 Event 0: <?xml version=“1.0” encoding=“UTF-8” ?> Event 1: <document> Event 2: Event 3:<title> Event 4: Java and XML Event 5: </title> Event 6: Let’s begin, new! Event 7: </document> Event 8: ENDDCUMENT
XMLEventの種類の判定 XMLEvent に定義されたメソッド isStartElement() など boolean型の返り値 if( event.isStartDocument() ) { return "Start Document”; } else if( event.isEndDocument() ) { return "End Document"; :
各イベントごとのメソッド StartDocument sdevent; String version = sdevent.getVersion(); return "Start Document:version=" + version; if( event.isStartDocument() ) { String version = ((StartDocument)event).getVersion(); }
各イベントごとのメソッド(2) Characters テキストの内容 getData() 改行・空白のみかの判定 isWhiteSpace() if( event.isCharacters() ) { String data=""; if( ((Characters)event).isWhiteSpace() ) data = "White Space"; else data = ((Characters)event).getData(); return "Text:" + data;
各イベントごとのメソッド(3) StartElement 要素の名前 getName() 開始タグ内の属性リスト getAttributes() 属性を持つ要素の例 <image source="java.png" height="400" > </image>
前回の例:要素ごとの数(1) 後で必要になる情報を記憶する(テーブルに) 複数のデータを記憶 java.util.Collection, java.util.List, java.util.Set 複数のペアのデータの表 java.util.Map (キーと値、キー全体は Set ) キー <document> <tytle> <image> <text> 値 1 2 4
前回の例:要素ごとの数(2) 変数ではなく Mapを用意 Map<String,Integer> map = new HashMap<String,Integer>(); while( reader.hasNext() ) { XMLEvent event = reader.nextEvent(); if( event.isStartElement() ) { String name = ((StartElement)event) .getName().toString(); saveToTable( name, map ); }
前回の例:要素ごとの数(3) Mapへの格納方法 // 既に格納されている時ー 値を1増やす // 既に格納されている時ー 値を1増やす if( map.containsKey( name ) ) { Integer i = map.get( name ); map.put( name, new Integer( i+1 ) ); } // 格納されていないー新たに値1で格納 else { map.put( name, new Integer( 1 ) );
前回の例:要素ごとの数(4) Mapに記憶された値の利用 Set<String> keys = map.keySet(); Iterator<String> it = keys.iterator(); while( it.hasNext() ) { String key = it.next(); Integer value = map.get( key ); System.out.println( key + "=" + value ); }
補足:拡張 forループの利用 Collection の処理を簡潔に記述 Set<String> keys = map.keySet(); for( String key : keys ) { Integer value = map.get( key ); System.out.println( key + "=" + value ); } カウンタ変数、Iterator は表に出ない
フィルタ的な処理の実現 「フィルタ」とは? 与えられた入力内容を変換して出力 入力=>[処理内容]=>出力 例: 一部分の取り出し 並べ替え 特定のパターンの検出 特定のパターンの変換
今回の目的(1) 平文のテキスト部分を取り出す 特定の名前の要素を取り出す 特定の名前の要素名を変換する
平文テキストの取り出し Characters のイベントを処理 getData()メソッドで内容を取り出す if( event.isCharacters() ) { Characters chars = (Characters)event; if( !chars.isWhiteSpace() ) { String text = chars.getData(); System.out.println( text ); }
特定の名前の要素を取り出す(1) 対象はあらかじめテーブルに記憶 Map<String,String> map = new TreeMap<String,String>(); map.put( "document" , "body" ); map.put( "title" , "h1" ); map.put( ":text" , "p" );
特定の名前の要素を取り出す(2) startElement, endElementを処理 getName() メソッドでチェック StartElement se; String name = se.getName().toString(); if( map.containsKey( name ) ) { String value = map.get( name ); System.out.println( "<" + value + ">" ); }
補足1)イベントの場合分け 処理をメソッドに分割する工夫 XMLEvent event = reader.nextEvent(); if( event.isStartElement() ) { startElement( (StartElement)event ); } else if( event.isEndElement() ) { endElement( (EndElement)event ); } :
補足2)「名前空間」 要素などの名前の衝突を避ける工夫 「名前空間(Namespace)」=「住所」 URN,URI で表現 xmlns:ns=http://w3.org/Namespace 「省略形」として「プレフィックス」 xmlns:v="urn:schemas-microsoftcom:vml" : <v:ab> abcd </v:ab>
補足3)QName javax.namespace.Qname クラス 正式なURIを含む情報を定められた形式で記憶 getLocalPart() -- 名前空間を含まない部分の名前 getPrefix() – 文書内のプレフィックスの名前
さらに応用) MS Office のXML MicroSoft Office 2007 からファイルの保存形式の標準は XML 複数の XMLを ZIP形式で保存 word -+- document.xml +- fontTable.xml +- settings.xml +- styles.xml +- WebSettings.xml
補足) ZIP と Jar コマンド Jar ( Java Archive ) ファイルの保存形式はZIPを採用 t オプション – 内容を調べる x オプション – 実際に展開 使用例) jar tvf Java.docx jar xvf Java.docx
補足) java.util.zipパッケージ ZIP形式のファイルを直接処理する ZipFile クラス – ZIP形式のファイル ZipEntry クラス – 格納された個々のファイルやディレクトリ ZipEntry の集まりは java.util.Enumeration (古いスタイルだが、取り扱いは java.util.Iterator とほぼ同じ)