xml読み込み用digester-rules

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


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

使用例

読み込む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();
    }
   ... 以下委譲メソッドが続く
}



環境
解説
  • 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を毎回書かなくてよい楽さは異常なので重宝しますよ。


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值