Hippo2000 (2000/7/19)
SOAP::GenericInputStream - SOAP::Parser 出力のためのデフォルトハンドラ
use SOAP::Parser;
my $parser = SOAP::Parser->new();
$parser->parsefile('soap.xml');
my $headers = $parser->get_headers();
my $body = $parser->get_body();
概要でご覧のように、直接SOAP::GenericInputStreamを使いません。むしろSOAP::ParserがSOAPドキュメントをアンマーシャルするために必要なとき、そのインスタンスを生成します。
このドキュメントの大きな目的は、SOAP::GenericInputStreamが生成するよりも何かもっと魅惑的なものを生成するパーサを持ちたいのであれば、このこのインタフェースを実装する必要があるので、SOAP::GenericInputStreamから公開されているインターフェースについて説明することです。
TypeUri と TypeName はアンマーシャルされるオブジェクトの型を示す文字列です。Resolver は1つの引数を取り、オブジェクトを解決する関数へのポインタです。そしてtermの実装ではこのポインタを通して呼び出さなければなりません(つまりtermが呼ばれるまでこれを保管しなければ習いことを意味します)。以下の例は最小限の実装、$self->{object}にオブジェクト・リファレンスを格納したと考えています:
sub new {
my ($class, $typeuri, $typename, $resolver) = @_;
return bless { resolver => $resolver }, $class;
}
sub term {
my ($self) = @_;
$self->{resolver}->($self->{object});
}
SOAP::Parser はこの関数を単純な(スカラ)アクセサに出会ったときに呼びます。あなたはそのアクセサのuriと名前の両方、そしてすべてのxsi:type属性を知らせられます。もしアンマーシャルされるパケットが名前空間を使っていなければ(これは可能性はありますがSOAP仕様では推奨されていません)、AccessorUriは未定義になります。明示的なxsi:type,がなければ、TypeUriとTypeNameもみ定義になります。そのためAccessorNameとContentの2つだけが定義されることが保証されています。
AccessorUri と AccessorName は名前空間と要素の名を与えます、そしてContentにはスカラの内容(常に文字列)が入ります。
SOAP::Parser は、複合アクセサ(例えば、その値がアクセサの下に書かれた構造化された型)に出会ったときこの関数を呼び出します。ここでの最初の4つのパラメータは上記のsimple_accessorで説明されたとおりです。IsPackageはこのノードがパッケージであるかどうかを示すヒントです(一般的にこれを無視することができます;SOAP::Parserがパッケージを扱うためのすべての作業をします)。Resolverは定義してもしなくてもかまいません。どのように機能するかを簡単に説明します。
この関数は同じインターフェースを実装したblessされたオブジェクト・リファレンスを返さなければなりません。($selfを単純に返すことをなにも禁止しませんが、SOAP::Parserがノード毎を基本にこれらのオブジェクト・リファレンスを追跡するので、これはあなたのクラスのインスタンスを単に作成し、そして各インスタンスは1つのオブジェクトをアンマーシャルする方法を知っている方が通常は簡単です。)。
Resolverが定義されていれば、Parserにオブジェクト・リファレンスが解決することを伝えるために新しいストリームがtermされたときにそれを呼び出す必要があります。そのためアンマーシャルをするために作成した新しいストリームにこのリファレンスを確実に伝えてください。
このため同時に新しいオブジェクトが作成されたときに知らせられる必要があります、通常Resolverを新しいストリームに直接渡すのではなく、むしろオブジェクトになにかを行い、パーサから渡されたらResolverにつなげるような独自のResolverの実装を提供するでしょう:
sub compound_accessor {
my ($self, $accessor_uri, $accessor_name, $typeuri, $typename, $is_package, $resolver) = @_;
my $object = $self->{object};
# 新しい入力ストリームに渡すクロージャを作成
my $my_resolver = sub {
my ($newly_unmarshaled_object) = @_;
# オブジェクトそのものでなにかする
$object->{$accessor_name} = $newly_unmarshaled_object;
# もし定義されていればParserのリゾルバにつなげる
$resolver->($child_object) if $resolver;
};
return $self->{type_mapper}->get_deserializer($typeuri, $typename, $my_resolver);
}
SOAP::Parser はすでにアンマーシャルされているオブジェクトへのリファレンスに出会ったらこの関数を呼びます。AccessorUriとAccessorNameはsimple_accessorのものと同じです。Objectは問題のものへのリファレンスです;これは基本的に他のストリーム(おそらくあなた実装した)がそれをアンマーシャルしたときに、解決されたものならなんでもです。これはblessされたオブジェクト・リファレンスにすることも、単にスカラへのリファレンスにもできます(SOAPでは複数のリファレンス スカラへのポインタを通信することができます)。どんな場合でも、新しいリファレンスをオブジェクト・グラフい追加しなければなりません。以下に簡単な例を示します:
sub reference_accessor {
my ($self, $accessor_uri, $accessor_name, $object) = @_;
$self->{object}{$accessor_name} = $object;
}
SOAP::Parser はまだアンマーシャルされていないオブジェクトへのリファレンス(前方リファレンス forward reference)にであったときに、この関数を呼びます。1つの引数(アンマーシャルされたオブジェクト)を期待する関数ポインタを返さなければなりません。これはあなた自身にreference_accessorの呼び出しを単に遅らせるクロージャを作成するのと同じくらい簡単になります:
sub forward_reference_accessor {
my ($self, $accessor_uri, $accessor_name) = @_;
# return a closure to complete the transaction at a later date
return sub {
my $object = @_;
$self->reference_accessor($accessor_uri, $accessor_name, $object);
};
}
SOAP::Parser は与えられたノードにもう何もアクセサがないときにこの関数を呼びます。この時点でアンマーシャルされたオブジェクト・リファレンスを親にわたすために、構築時に渡されたResolverを呼ぶことが期待されます。
前方リファレンスのために、そのオブジェクトはまだ完全ではないかもしれない(それはまだ解決されない未解決の前方リファレンスを持っているかもしれない)ことに注意してください。これは問題ではありません。というのもパーサはまだ完了していないからです、そしてforward_reference_accessorの実装からこれらのオブジェクト・リファレンスを修正するリゾルバを提供している限り、パーサが完了するときに、パーサによってあなたのオブジェクトはすべての解決されるでしょう。
この関数の実装例はnew()の説明をご覧下さい。
SOAP::TypeMapper
Keith Brown
perl(1).
ご意見、ご質問はこちらの掲示板で受け付けています。
またメールは河馬屋(Nifty)にお願いします。