xml読み込み用digester-rules

来源:互联网 发布:阿里巴巴域名 编辑:程序博客网 时间:2024/04/30 08:41

設定ファイルをXMLで書く事って多いんだけど、javaでXML使う時って型安全や妥当性に拘りすぎて仰々しいと時々思う。かといってPropertiesだと階層的なデータは表現し辛い。JSONやYAMLやObjective-Cのplistみたいに手軽に階層的なデータが使えると嬉しいと思う。


ということでCommonsDigester(初めて使った)でそれっぽいものを書いてみた。解説などは後ろで。

使用例

読み込むXMLファイル。レポートメールの設定ファイルのイメージで。

<?xml version = "1.0" encoding = "UTF-8" ?><map><string key="smtpHost">mx010</string><integer key="smtpPort">25</integer><string key="fromAddress">terazzo@example.com</string><string key="subject">Report Mail</string><list key="bccAddresses"><string>admin1@example.com</string><string>admin2@example.com</string><string>admin3@example.com</string></list><string key="templatePath">mail_template/ReportMail.html</string></map>

読み込みプログラム

        String configPath = "config/sample.ReportMailer";        Map map = new Configuration(new File(configPath).toURL());        System.out.println("" + map);

出力結果(途中で適宜改行している)

{    templatePath=mail_template/ReportMail.html,    smtpHost=mx010,     smtpPort=25,    fromAddress=terazzo@example.com,    bccAddresses=[admin1@example.com, admin2@example.com, admin3@example.com],     subject=Report Mail}

ソースコード

ルールファイル。もう少し簡単にかけそうだけど……

src/sample/config/collections-rule.xml:

<?xml version = "1.0" encoding = "UTF-8" ?>    <digester-rules>  <object-create-rule pattern="map" classname="java.util.HashMap"/>  <pattern value="*/map/string">      <call-method-rule methodname="put" paramcount="2" paramtypes="java.lang.String,java.lang.String"/>      <call-param-rule paramnumber="0" attrname="key"/>      <call-param-rule paramnumber="1"/>   </pattern>  <pattern value="*/map/integer">      <call-method-rule methodname="put" paramcount="2" paramtypes="java.lang.String,java.lang.Integer"/>      <call-param-rule paramnumber="0" attrname="key"/>      <call-param-rule paramnumber="1"/>   </pattern>  <pattern value="*/map/map">      <object-create-rule classname="java.util.HashMap"/>      <call-method-rule targetoffset="1" methodname="put" paramcount="2" />      <call-param-rule paramnumber="0" attrname="key"/>      <call-param-rule paramnumber="1" from-stack="true"/>   </pattern>  <pattern value="*/map/list">      <object-create-rule classname="java.util.ArrayList"/>      <call-method-rule targetoffset="1" methodname="put" paramcount="2" />      <call-param-rule paramnumber="0" attrname="key"/>      <call-param-rule paramnumber="1" from-stack="true"/>   </pattern>  <pattern value="*/list/string">      <call-method-rule methodname="add" paramcount="1" paramtypes="java.lang.String"/>      <call-param-rule  paramnumber="0" />   </pattern>  <pattern value="*/list/integer">      <call-method-rule methodname="add" paramcount="1" paramtypes="java.lang.Integer"/>      <call-param-rule  paramnumber="0" />   </pattern>  <pattern value="*/list/map">      <object-create-rule classname="java.util.HashMap"/>      <call-method-rule targetoffset="1" methodname="add" paramcount="1"/>      <call-param-rule  paramnumber="0" from-stack="true" />   </pattern>  <pattern value="*/list/list">      <object-create-rule classname="java.util.ArrayList"/>      <call-method-rule targetoffset="1" methodname="add" paramcount="1"/>      <call-param-rule  paramnumber="0" from-stack="true" />   </pattern></digester-rules>

読み込み用クラス

package sample.config;import java.net.URL;import java.util.Collection;import java.util.Map;import java.util.Set;import org.apache.commons.digester.Digester;import org.apache.commons.digester.xmlrules.DigesterLoader;/** * collections-rule.xmlを使ったDigesterによって設定ファイルを読み込むクラス */public class Configuration implements Map {        private static final String RULE_XML_NAME = "collections-rule.xml";    /**     * 設定ファイル読み込み用のDigester     */    private static Digester digester =        DigesterLoader.createDigester(Configuration.class.getResource(RULE_XML_NAME));        private URL url;    private Map map;    public Configuration(URL url) {        super();        this.url = url;    }    private synchronized Map getMap() {        if (needsReloading()) {            this.map = load(url);        }        return this.map;    }    /**     * urlからdigesterを使用してMapを読み込む     * @param url URL     * @return urlから読み込んだMap     */    protected Map load(URL url) {        try {            return (Map)digester.parse(url);        } catch (RuntimeException e) {            throw e;        } catch (Exception e) {            throw new RuntimeException("Exception occurs while loading configuration.", e);        }    }    /**     * @return 再読み込みが必要ならtrueを戻す     */    protected boolean needsReloading() {        return this.map == null;    }    /**     * mapのtoString()の結果を戻す     */    public String toString() {        return getMap().toString();    }    /**     *      * @see java.util.Map#clear()     */    public void clear() {        getMap().clear();    }   ... 以下委譲メソッドが続く}


環境

  • J2SE 5.0
  • commons-digester-1.8.jar
  • commons-logging-1.0.4.jar
  • commons-beanutils-core-1.7.0.jar

解説

  • map/listが再帰的に使える。mapはHashMap、listはArrayListに変換される。
  • ルートエレメントはmap
  • map/list内にstring, integerを含める事が出来る
  • map内のエレメントは、key=で指定された文字列をキーにHashMapにセットされる
  • ConfigurationクラスはMapインタフェースを実装。但し読み込んだHashMapに委譲しているだけ。

その他

Commons Configurationを使いたかったけど、キーに対する値をMapで欲しい時って結構多いので微妙だ。(config.getMap("key")が欲しいのよ。)


あんまりMapをベタに使うのってjava wayじゃないよね。でもruleを毎回書かなくてよい楽さは異常なので重宝しますよ。


とは言えせめてキーぐらいはリテラルで書かずに使えるようにしたい。ということでその辺は次回。

原创粉丝点击