Struts の DynaActionForm で、複数の項目によるプロパティを扱うには

表題だけでは何のことか分からないと思いますが、画面上では複数の入力項目であるが、フォームのプロパティとしては一つである場合のようなパターンです。

たとえば、Web アプリで画面上の入力項目として、下記の例のように、郵便番号を配達局番号と町域番号に分けて表示したいとします。

(HTML例)
  郵便番号:  - 

これを、素直に Struts の HTML タグを使用した JSP で記述すると以下のようになると思います。

(JSP 例 - 変更前)
<html:form action="/Test">
  郵便番号: <html:text property="postOfficeCode"/> - <html:text property="townAreaCode"/>
</html:form>

この入力フォームに対応する Struts のフォーム定義を DynaActionForm を使用して記述した場合、以下のようになると思います。

(フォーム定義 - 変更前)
<form-bean name="testForm" type="org.apache.struts.action.DynaActionForm">
  <form-property name="postOfficeCode" type="java.lang.String"/>
  <form-property name="townAreaCode" type="java.lang.String"/>
</form-bean>

上記のようなフォーム定義で何か問題があるわけではないです。
しかし、私の場合、最終的にドメインモデルへマッピング*1することを考えると、意味的に一つの項目は、一つのプロパティとしてまとまっていて欲しいと思うのです。

そこで、以下のような郵便番号を表す JavaBeans *2を用意して、フォームのプロパティとして使用します。

(郵便番号JavaBeans)
public class PostalCode implements java.io.Serializable {
  private String postOfficeCode;
  private String townAreaCode;

  // セッタ、ゲッタの記述を省略
}

フォームの定義は以下。

(フォーム定義例 - 変更後 1)
<form-bean name="testForm" type="org.apache.struts.action.DynaActionForm">
  <form-property name="postalCode" type="PostalCode"/>
</form-bean>

このままでは、HTML フォーム内の入力項目と値のやり取りが出来ないので、JSP の記述も以下のようにします。

(JSP 例 - 変更後)
<html:form action="/Test">
  郵便番号: <html:text property="postalCode.postOfficeCode"/> - <html:text property="postalCode.townAreaCode"/>
</html:form>

Sturts ではネストしたプロパティへのアクセスを、プロパティ名を '.'(ドット) で繋ぐことで行なえるようになっています。そこで、入力項目のプロパティ名を、フォームのプロパティ名 + ドット + 郵便番号JavaBeans内プロパティ名 とすることで、対象となるJavaBeansの各プロパティに値を設定できます。

この状態のままでも問題は無いのですが、郵便番号を表す JavaBeans を作成するのも( ゜Д゜)マンドクセーだとなった場合、フォーム定義を以下のようにすることで郵便番号を表す JavaBeans を使う必要がなくなります。*3

(フォーム定義 - 変更後 2)
<form-bean name="testForm" type="org.apache.struts.action.DynaActionForm">
  <form-property name="postalCode" type="org.apache.commons.beanutils.LazyDynaBean"/>
</form-bean>

プロパティの型を BeanUtil に含まれている LazyDynaBean にすることで、必要に応じてプロパティが追加されます。
Struts がフォームに対して、postalCode.postOfficeCode プロパティを設定しようとした場合、postOfficeCode プロパティが LazyDynaBean オブジェクトに追加され、同様にpostalCode.townAreaCode プロパティを設定しようとした場合には townAreaCode プロパティが追加されるというわけです。

*1:実際には郵便番号をあらわすVOにマップすることを想定しています。マッピング処理も自動でとか。

*2:'VO' ではなく、'JavaBeans' としているのは意図的にです。'VO' はイミュータブル!!

*3:もっとも、DynaBean は プロパティアクセスを行なう局面では便利だと思いますが、Java のコード上では Map と大差ないので好きではありません。DynaActionForm についても同様です。特別な理由がない限り、ActionForm クラスを作るべきだと私は考えています。型制約を強めることができますし。