Codin’ In The Free World

2009-12-16

[] ActionScript XML SAX パーサ

ActionScriptはE4Xを実装してるので、メモリ食ってもいい環境で

DOM操作をしたいときは(名前空間が絡まなければ)比較的簡単なのですが、

ストリームの処理をしたいときには困ります。


最近のリアルタイム系コンテンツで使いたいということで、as3saxparserというのを書きました。

http://github.com/lyokato/as3saxparser


ハンドラの準備

ハンドラはIXMLSAXParserインタフェースを実装している必要があります。

IXMLSAXParserインタフェースを直接実装するか、

XMLSAXDefaultHandlerを継承するかします。


IXMLSAXParserインタフェースを直接実装する場合は次のようになります。

import org.coderepos.xml.sax.IXMLSAXParser;
import org.coderepos.xml.XMLAttributes;

public class MyHandler implements IXMLSAXParser
{
  ...

  public function startDocument():void { ... }
  public function endDocument():void { ... }
  public function startElement(ns:String, localName:String, attrs:XMLAttributes, depth:uint):void { ... }
  public function endElement(ns:String, localName:String, depth:uint):void { ... }
  public function cdata(str:String):void { ... }
  public function comment(str:String):void { ... }
  public function characters(str:String):void { ... }
}

インタフェースが要求する関数は上の7つです。

だいたい見れば分かると思います。

startElementについてだけ後で解説します。


IXMLSAXHandlerを直接実装するのではなく、

XMLSAXDefaultHandlerを継承して、必要なメソッドだけオーバーライドしてもよいです。

import org.coderepos.xml.sax.XMLSAXDefaultHandler;
import org.coderepos.xml.XMLAttributes;

public class MyHandler extends XMLSAXDefaultHandler
{
  ...
  override public function startElement(ns:String, localName:String, attrs:XMLAttributes, depth:uint):void {...}
  override public function endElement(ns:String, localName:String, depth:int):void {...}
  override public function characters(str:String):void {...}
}

こちらの方が楽ですが、ActionScriptは多重継承をサポートしていないので、

他に継承したいクラスが存在するときには使えません。

その場合はIXMLSAXParserを直接implementsしてください。


最終的に次のように使います。


import org.coderepos.xml.sax.XMLSAXParser;

var parser:XMLSAXParser = new XMLSAXParser();
parser.handler = new MyHandler();

try {
  parser.pushBytes(xmlFragmentBytes1);
  parser.pushBytes(xmlFragmentBytes2);
  parser.pushBytes(xmlFragmentBytes3);
  ...
} catch (e:*) {
  if (e is XMLSyntaxError) {

  } else if (e is XMLFragmentSizeOverError) {

  } else if (e is XMLElementDepthOverError) {

  } else {
  
  }
}


まず、XMLSAXParserのオブジェクトを生成し、そのhandlerに、

先ほど準備した、独自のハンドラクラスのオブジェクトをセットします。


後は、ソケットなりファイルなりから読み込んだByteArrayのオブジェクトをpushBytesに渡していきます。

SAXパーサーなので、当然、XMLを最後まで読まなくても、随時イベントが発生し、対応するハンドラのコールバックが

呼ばれますし、読み終わったフラグメントは捨てていきます。

不正なXMLだったり、XMLのタグやテキストのサイズが大きすぎたり、elementの階層が深すぎたりすると、

例外が投げられるのでtry catchしておきます。


タグやテキストのサイズの限界値、elementの階層の深さの限界値は、編集可能です。


import org.coderepos.xml.sax.XMLSAXParserConfig;
import org.coderepos.xml.sax.XMLSAXParser;

var config:XMLSAXParserConfig = new XMLSAXParserConfig();
config.MAX_FRAGMENT_SIZE = 1024;
config.MAX_ELEMENT_DEPTH = 20;

var parser:XMLSAXParser = new XMLSAXParser(config);


コールバック

startElementをもう一度みてみます。

public function startElement(ns:String, localName:String, attrs:XMLAttributes, depth:uint) {
  ...
}

attrsだけ説明が必要ですね。

<foo xmlns="http://example.com" xmlns:bar="http://example.com/bar" hoge="aaa" bar:huga="bbb"/>

例えばこのようなXML要素をパースしたときに呼ばれるstartElement内では、属性値を次のように取得できます。

attrs.getValue("hoge"); // aaaが返る
attrs.getValueWithPrefix("bar", "huga"); // bbbが返る

取得した属性を全部トレースしたければ次のように出来ます。

import org.coderepos.xml.XMLAttribute;

var attrArray:Array = attrs.toArray();
for each(var attr:XMLAttribute in attrArray) {
  trace(attr.uri);
  trace(attr.name);
  trace(attr.value);
}

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/lyokato/20091216/1260944034