plistっぽいxml読み込み用digester-rules
(2008/5/24 追記: 本物のplist読み込みについては真・plist形式のxml読み込み用digester-rules(およびクラス) - terazzoの日記を参照)
設定ファイルをXMLで書く事って多いんだけど、javaでXML使う時って型安全や妥当性に拘りすぎて仰々しいと時々思う。かといってPropertiesだと階層的なデータは表現し辛い。JSONやYAMLやObjective-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(); } ... 以下委譲メソッドが続く }
環境
- J2SE 5.0
- commons-digester-1.8.jar
- commons-logging-1.0.4.jar
- commons-beanutils-core-1.7.0.jar