- PR -

SAXParserの逐次処理について

投稿者投稿内容
Metaphor
会議室デビュー日: 2003/07/31
投稿数: 9
投稿日時: 2003-07-31 14:02
以下のようなコードで、外部プロセスの標準出力からXMLを解析したいのですが、この外部コマンドが時間間隔をおいて出力してくるのを逐次処理したいのです。
実際にやってみると、ストリームが閉じられるまで解析が始まらず困っています。
PAXではそういったことはできないのでしょうか?
お知恵を拝借させてください。

Process proc = Runtime.getRuntime().exec( "外部プロセスのコマンド" );
SAXParserFactory spFactory = SAXParserFactory.newInstance();
SAXParser parser = spFactory.newSAXParser();
parser.parse( proc.getInputStream(), new MyHandler() );
ocean
ベテラン
会議室デビュー日: 2003/07/06
投稿数: 65
投稿日時: 2003-07-31 16:42
SAXParser(JDK1.4.2)の動作を解析してみたところ、8192byteの内部バッファが埋まるまでは解析を始めないようです。外部プロセスの出力するサイズが8192byteを越えた時初めて処理されるので、逐次処理をするにはこの"8192byte"を変更しないといけないと思います。(できるかどうかはわかりません。SAXParser#setProperty()でできるのかもしれませんが・・・)

コード:


import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

class MeApp
{
public static void main(String[] args) throws Exception
{
final SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

final Process process = Runtime.getRuntime().exec("main.exe");

try
{
parser.parse(
new MeInputStream(process.getInputStream()),
new MeHandler()
);
}
finally
{
process.destroy();
}
}
}

class MeInputStream extends InputStream
{
private final InputStream _stream;

public MeInputStream(InputStream stream)
{
_stream = stream;
}

public void close() throws IOException
{
_stream.close();
}

public int read() throws IOException
{
final int ret = _stream.read();
System.out.println(" read() => " + ret);
System.out.flush();
return ret;
}

public int read(byte[] b) throws IOException
{
final int ret = _stream.read(b);
System.out.println(" read(byte[]) " + b.length + " => " + ret);
System.out.flush();
return ret;
}

public int read(byte[] b, int off, int len) throws IOException
{
final int ret = _stream.read(b, off, len);
System.out.println(" read(byte[], int off, int len) "
+ b.length + ", " + off + ", " + len + " => " + ret);
System.out.flush();
return ret;
}

public long skip(long n) throws IOException
{
final long ret = _stream.skip(n);
System.out.println(" skip(long) " + n + " => " + ret);
System.out.flush();
return ret;
}
}

class MeHandler extends DefaultHandler
{
public void startDocument()
{
System.out.println("startDocument");
System.out.flush();
}

public void endDocument()
{
System.out.println("endDocument");
System.out.flush();
}

public void startElement(String uri, String localName, String qName, Attributes attributes)
{
System.out.println("startElement: " + qName);
System.out.flush();
}

public void endElement(String uri, String localName, String qName)
{
System.out.println("endElement: " + qName);
System.out.flush();
}
}





[ メッセージ編集済み 編集者: ocean 編集日時 2003-07-31 17:46 ]
ocean
ベテラン
会議室デビュー日: 2003/07/06
投稿数: 65
投稿日時: 2003-07-31 17:06
IBMのページでは、Apache Xercesなら

parser.setProperty("http://apache.org/xml/properties/input-buffer-size",
new Integer(2048));

で、できるように書かれていますね。JDK1.4.2ではプロパティを認識できませんでした。


[ メッセージ編集済み 編集者: ocean 編集日時 2003-08-01 00:19 ]
Metaphor
会議室デビュー日: 2003/07/31
投稿数: 9
投稿日時: 2003-08-02 03:52
返答ありがとうございます。
もし変更可能であった場合には、極端には1バイトにすれば逐次処理になるんでしょうか。
JDKのドキュメントをみると、SAXParser#setProperty(String, Integer)ってないですね。
この辺って統一されてないんですね(^^;
ocean
ベテラン
会議室デビュー日: 2003/07/06
投稿数: 65
投稿日時: 2003-08-02 11:16
引用:

もし変更可能であった場合には、極端には1バイトにすれば逐次処理になるんでしょうか。



うーん、よくわかりません。タグ名よりも短くても正常に動作するのか怪しい気もしますし・・・・Xercesを実際に試すのが確実だと思います。もしかすると、Xercesではバッファがいっぱいになる前に適宜処理してくれるのかもしれませんが、これも試していないのでわかりません。

引用:

JDKのドキュメントをみると、SAXParser#setProperty(String, Integer)ってないですね。
この辺って統一されてないんですね(^^;



ああ、これは SAXParser#setProperty(String, Object) の Object に Integer を渡すということだと思います。
ocean
ベテラン
会議室デビュー日: 2003/07/06
投稿数: 65
投稿日時: 2003-08-02 11:48
簡単に試せそうだったため、試してみました。結果、Xercesはバッファが一杯になっていなくても適宜解析しているため、バッファサイズを変えなくても逐次処理できました。

//////////////////////////////
// 試しかた

1. http://xml.apache.org/dist/xerces-j/Xerces-J-bin.2.5.0.tar.gzをダウンロードして、中にある Sample?.jar 以外の3つのjarを{jre}\lib\extに解凍する。
2. コンパイルはJDK1.4.2のものと同じに行う。
3. 実行時、java -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser MeApp のようにして、XercesをSAXとして使うよう指定する。(IBMのページより)

////////////////////////////////
// 試したXMLファイル

<?xml version="1.0"?>
<colors>
<red/><green/><blue/>
<red/><green/><blue/>
<red/><green/><blue/>
</colors>

////////////////////////////////
// プログラム

java: 上で投稿したもの
main.exe: 行ごとに読み込んで Sleep した後に標準出力に書き出す簡単なもの

///////////////////////////
// 出力結果

コード:
 read() => 60
 read() => 63
 read() => 120
 read() => 109
 read() => 108
 read() => 32
 read() => 118
 read() => 101
 read() => 114
 read() => 115
 read() => 105
 read() => 111
 read() => 110
 read() => 61
 read() => 34
 read() => 49
 read() => 46
 read() => 48
 read() => 34
startDocument
 read() => 63
 read() => 62
 read(byte[], int off, int len) 2048, 0, 2048 => 2
 read(byte[], int off, int len) 2048, 0, 2048 => 10
startElement: colors
 read(byte[], int off, int len) 2048, 0, 2048 => 23
startElement: red
endElement: red
startElement: green
endElement: green
startElement: blue
endElement: blue
 read(byte[], int off, int len) 2048, 0, 2048 => 23
startElement: red
endElement: red
startElement: green
endElement: green
startElement: blue
endElement: blue
 read(byte[], int off, int len) 2048, 0, 2048 => 23
startElement: red
endElement: red
startElement: green
endElement: green
startElement: blue
endElement: blue
 read(byte[], int off, int len) 2048, 0, 2048 => 11
endElement: colors
 read(byte[], int off, int len) 2048, 0, 2048 => -1
endDocument


ocean
ベテラン
会議室デビュー日: 2003/07/06
投稿数: 65
投稿日時: 2003-08-02 11:51
結局、バッファサイズは本質ではなく、解析のタイミングが問題だったみたいです。実装による解析のタイミングの違いがはっきり現れるのは、興味深かったです。(JDK1.4.2のSAXに必要なのは実装の変更なのでしょうか)でも、再コンパイルなしにSAXの切り替えができるのは、本当にクールです。


[ メッセージ編集済み 編集者: ocean 編集日時 2003-08-02 11:59 ]
Metaphor
会議室デビュー日: 2003/07/31
投稿数: 9
投稿日時: 2003-08-03 09:31
大変詳しいご説明ありがとうございます。
さっそくやってみます

#これでデスマーチに入らず夏休みが迎えられそうです……

スキルアップ/キャリアアップ(JOB@IT)