はじめまして,小林 (koichik) と申します.
Axis 1.4 で,TypeMappingImpl の doAutoTypes を true に
設定した場合に Map が適切にエンコードされないという現象に
遭遇しています.
以下,長文になりますが状況を詳しく説明します.
TypaMapping は TypeMappingDelegate を使ってチェーン状に
構成することができますが,仮に二つの TypeMappingDlegate が
以下のようにあるとして,
next
TypeMappingDelegate(1) -----> TypeMappingDelegate(2)
└TypeMappingImpl(1) └TypeMappingImpl(2)
TypeMappingImpl(1) はほとんど空ですが,Map を扱うために
MapSerializer/MapDeserializer を登録しています.
TypeMappingImpl(2) には標準のマッピングが登録されています.
# この TypeMapping チェーンは WSDDService#initTMR() で
# 作成されるものです.
そして,TypeMappingImpl(1) の doAutoTypes をtrue に
設定して利用しています.
このような構成で戻り値型が Map のメソッドを Java:RPC で
呼び出すと,Map のキーや値 (String) が xsd:base64Binary で
エンコードされてしまいます.
Axis の動きを追いかけてみたところ,まずは TypeMappingImpl(1) に
登録された MapSerializer がキーや値をシリアライズするために
SerializationContext#serialize() を呼び出します.
MapSerializer.java(96)
================================================================================
context.serialize(QNAME_KEY, null, key, null, null, Boolean.TRUE);
context.serialize(QNAME_VALUE, null, val, null, null, Boolean.TRUE);
================================================================================
ここではシリアライズ対象のオブジェクト (実際は String) である
key と val を渡していますが,第 4 引数の xmlType (QName) には
null を渡しています.
呼び出された SerializationContext はいくつかのメソッドを経て
TypeMappingDelegate(1) の getSerializer() を呼び出します.
TypeMappingDelegate(1) はそのまま TypeMappingImpl(1) に委譲します.
この時点でも xmlType は null のままです.
TypeMappingImpl.java(286〜294)
================================================================================
public javax.xml.rpc.encoding.SerializerFactory
getSerializer(Class javaType, QName xmlType)
throws JAXRPCException {
javax.xml.rpc.encoding.SerializerFactory sf = null;
// If the xmlType was not provided, get one
if (xmlType == null) {
xmlType = getTypeQName(javaType, null);
================================================================================
xmlType が null で呼び出されるため,getTypeQName() を呼び出します.
この時の第 2 引数 TypeMappingDelegate は null です.
getTypeQName() は XML 型を解決しようとしますが,TypeMappingImpl(1) は
String に対するマッピングを持っていません.
TypeMappingImpl(2) は String のマッピングを持っていますが,
getTypeQName() の第 2 引数で渡された next が null であるため,
チェーンの後続から解決することもできません.
そして TypeMappingImpl(1) は doAutoTypes が true に設定されて
いるため,String に対して BeanSerializer/BeanDesrializer を
登録してしまいます.
TypeMappingImpl.java(674〜689)
================================================================================
if (xmlType == null && shouldDoAutoTypes())
{
xmlType = new QName(
Namespaces.makeNamespace( javaType.getName() ),
Types.getLocalNameFromFullName( javaType.getName() ) );
/* If doAutoTypes is set, register a new type mapping for the
* java class with the above QName. This way, when getSerializer()
* and getDeserializer() are called, this QName is returned and
* these methods do not need to worry about creating a serializer.
*/
internalRegister( javaType,
xmlType,
new BeanSerializerFactory(javaType, xmlType),
new BeanDeserializerFactory(javaType, xmlType) );
}
================================================================================
この結果,String であるマップのキーや値が BeanSerializer により
xsd:base64Binary でエンコードされてしまうようです.
ここまで調査したのですが,問題が Axis の実装にあるのか,
上で説明した使い方にあるのかが分かりません.
TypeMappingDelegate(1) ではなく TypeMappingDelegate(2) の
doAutoTypes を true にすべきなのかと思い試してみましたが,
その場合 Map は適切に扱えるようになったものの,今度は
Bean の配列に対して TypeMappingImpl(1) が Object 配列の
Serializer を返してしまうなど,doAutoTypes による
BeanSerializer/BeanDeserializer の自動登録が適切に働かなく
なってしまいました.
ここで (ようやく) 質問なのですが,doAutoTypes を有効にした上で,
Map など個別に登録した Serializr/Deserializer も適切に扱うには
どのようにすべきなのでしょうか?
正しいやり方が分からないので,現在はとりあえず次のように
回避しています.
xmlType が null のまま TypeMappingDelegate(1) が呼ばれると
TypeMappingImpl(2) に登録されているマッピングが使われないので,
TypeMappingDelegage(1) の前に TypeMappingDelegate サブクラスを
TypeMappingDelegate(0) として登録しました.
TypeMappingDelegate サブクラスでは getSerializer() をオーバーライドし,
TypeMappingImpl に委譲する前に xmlType を解決します.
================================================================================
public SerializerFactory getSerializer(Class javaType, QName xmlType)
throws JAXRPCException {
if (xmlType == null) {
xmlType = getTypeQName(javaType);
}
return super.getSerializer(javaType, xmlType);
================================================================================
これにより,TypeMappingImpl(2) に登録されている型が
適切にシリアライズされるようになりました.
doAutoTypes による BeanSerializer/BeanDeserializer の
自動登録も問題ありません.
ただし,TypeMappingDelegate のコンストラクタは可視性が
指定されていないため (package スコープ),やむを得ず
勝手に org.apache.axis.encoding パッケージを使っています.
もし TypeMappingDelegate のサブクラスを作成するやり方が
適切なのであれば,コンストラクタを public にして頂けると
ありがたいです.
以上,かなりの長文になってしまいましたが,doAutoTypes を有効にして
Map も扱うための適切な方法があればご教示頂けないでしょうか.
よろしくお願いします.
--
<component name="koichik">
<property name="fullName">"Koichi Kobayashi"</property>
<property name="email">"[EMAIL PROTECTED]"</property>
<property name="blog">"http://d.hatena.ne.jp/koichik"</property>
</component>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]