XMファイルの書き方
Javaプログラミングでよく使用するXMLファイルですが、 かなり使える優れものというところをアピールしたく。この記事を書くことにしました。
※本当は、XMLの勉強をしたからです。
XMLとは
オラクルによると下記のようなものです。
しかし、WebLogicサーバー(ウェブサーバー)用のドキュメントのようです。
Extensible Markup Language (XML)とは、ドキュメント内のデータの内容および構造を示すのに使用するマークアップ言語です。
日本語にすると「拡張マークアップ言語」であり、とりあえずは、「拡張するのに使えるよ」というところです。
どうやって拡張するの?
同じようにオラクル社のドキュメントでは下記のようにあります。
ML ドキュメントの記述方法には、DTD および スキーマの 2 種類があります。
XML ドキュメントを定義する DTD
<!DOCTYPE address_book [
<!ELEMENT person (name, address?, phone?, email?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT address (street, city, state, zip)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT email (#PCDATA)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zip (#PCDATA)>
<!ATTLIST person gender CDATA #REQUIRED>
<!ATTLIST phone area_code CDATA #REQUIRED>
]>
XML ドキュメントを定義する スキーマ(Schema)
オラクル社によると以下の通りです。
スキーマは、XML 仕様において最近開発され、DTD に取って代わるものとされています。
スキーマでは、DTD および DTD 以外の XML ドキュメント自体より柔軟かつ詳細に XML ドキュメントを記述します。スキーマ仕様は、現在 World Wide Web Consortium(W3C)によって開発中であり、DTD の制限の多くを克服するものとされています。XML スキーマの詳細については、http://www.w3.org/TR/xmlschema-0/ を参照してください。
XML Schema【XSD】
XML Schemaとは、XML文書の構造を定義するスキーマ言語の一つで、Web技術の標準化を進めるW3C(World Wide Web Consortium)が勧告したもの。 XML自体を用いてスキーマを記述する。
<xsd:schema xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<xsd:element name="address_book" type="bookType"/>
<xsd:complexType name="bookType">
<xsd:element name=name="person" type="personType"/>
</xsd:complexType>
<xsd:complexType name="personType">
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="address" type="addressType"/>
<xsd:element name="phone" type="phoneType"/>
<xsd:element name="email" type="xsd:string"/>
<xsd:attribute name="gender" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="addressType">
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:string"/>
</xsd:complexType>
<xsd:simpleType name="phoneType">
<xsd:restriction base="xsd:string"/>
<xsd:attribute name="area_code" type="xsd:string"/>
</xsd:simpleType>
</xsd:schema>
規定の使い方
上記のファイルでタグの意味を定義付けていろいろな用途に使用するというのが、世の中にある「通常の使い方」です。
Javaプログラマがよく目にするのは、フレームワークで使用する設定ファイル「config.xml」、ウェブサーバーで使用する「web.xml」などがあると思いますが、列挙すると義理がありません。
しかし、ここではJavaをメインに記載していますので、Javaテクノロジーで使用しているXMLについてどのようなものがあるか以下に列挙します。
- FXML: JavaFXで使用する画面表示のコンポーネントを記述するものです。これには、GUIエディタ「SceneBuilder」があります。
- CID設定ファイルを使用してJavaEEでの依存性の注入を行っています。
色々ありますが、XMLファイルでクラスを指定して、そのクラスのインスタンスを取得する。DBのORマッピングを行うなどがあります、
使い方1:デコーダーとエンコーダー
XMLDecoderとXMLEncoderというクラスがりますが、これもクラスをXMLファイルに出力したり、読み込んだりするクラスです。
使い方2:パーサー
使うのは、DocumentBuilderFactoryというクラス(API)で、下のような形で使用します。
/**
* XMLドキュメント(ファイル)を読み込む。
* @param directory ファイルの配置しているディレクトリ
* @param fileName ファイル名
* @return Documentオブジェクト
* @throws RpgException ファイルの読み込みエラー
*/
private static Document loadDocumentBuilder(String directory, String fileName) throws Exception {
//creating a constructor of file class and parsing an XML files
Path path = Paths.get(directory, fileName);
File file = path.toFile();// ("/Monster.xml");
//an instance of factory that gives a document builder
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//an instance of builder to parse the specified xml file
DocumentBuilder db = null;
Document doc = null;
try {
db = dbf.newDocumentBuilder();
doc = db.parse(file);
doc.getDocumentElement().normalize();
} catch (ParserConfigurationException e) {
}
return doc;
}
早い話が、DocumentBuilderFactoryクラスで、ファイルを読み込んで、タグを取得するというものです。
XMLファイルを使用するのに、どちらが便利か?
今回は、タグの名前を自由に決めたいので、「使い方2」を採用します。
以前、作成したXMLUtilでは、下のようにXMLを読み込んで、クラスを生成するという手順を踏みました。最終的に、XMLに対応したクラスを作成しておき、その値をXMLファイルから取得するという形での利用でした。
/**
* Job.xmlを読み込む。必ず、Command.xmlを読み込んでから実行する。
* @return Jobリスト
* @throws RpgException XMLの設定エラー
*/
public static Map<String, RpgJob> loadJobs(String directory) throws RpgException {
Map<String, RpgJob> jobMap = new HashMap<>();
Map<String, RpgCommand> cmdMap = RpgConfig.getInstance().getCommandMap();
if (cmdMap.size() <= 0) {
throw new RpgException(MessageConst.ERR_BEFORE_LOAD_CMD.toString());
}
try {
Document doc = loadDocumentBuilder(directory, "Job.xml");
if (isDebug) System.out.println("Root element: " + doc.getDocumentElement().getNodeName());
NodeList list = doc.getElementsByTagName("job");
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
RpgJob j = createJob(node, cmdMap);
jobMap.put(j.getJobId(), j);
}
}
} catch (RpgException e) {
e.printStackTrace();
throw new RpgException(MessageConst.ERR_XML_PERSE.toString() + ": " + e.getMessage());
}
return jobMap;
}
/**
* RpgJobクラスを生成する。依存するCommandListをセット。
* @param node jobタグ
* @return RpgJob 職業クラス
* @throws RpgException XML設定エラー
*/
private static RpgJob createJob(Node node, Map<String, RpgCommand> map) throws RpgException {
Element e = (Element) node;
String id = getTagNode(e, "id").item(0).getTextContent();
String name = getTagNode(e, "name").item(0).getTextContent();
String disc = getTagNode(e, "discription").item(0).getTextContent();
String commandList = getTagNode(e, "commandList").item(0).getTextContent();
String[] list = commandList.split(",");
List<RpgCommand> cmdList = new ArrayList<>();
for (String cmd : list) {
RpgCommand cmdCls = map.get(cmd.trim());
cmdList.add(cmdCls);
}
// RogConfigの値を変えないように気を付ける
Map<String, RpgStatus> templateMap = RpgConfig.getInstance().getStatusMap();
Map<String, RpgStatus> statusMap = new HashMap<>();
statusMap.putAll(templateMap);
Set<String> set = statusMap.keySet();
// ステータス上昇値の設定
for (String key : set) {
if (isDebug) System.out.println("Status: " + key);
String statsUp = getTagNode(e, key.toLowerCase()).item(0).getTextContent();
RpgStatus st = new RpgStatus();
st.setKigo(key);
st.setValue(Integer.parseInt(statsUp));
statusMap.put(key, st);
}
RpgJob job = null;
job = new RpgJob(id, name, disc, cmdList);
// ステータス上昇値のマップを設定する。
job.setStatusUpMap(statusMap);
return job;
}
これを、使用してフレキシブルな形でクラスの生成をできるようにしたいと思っています。
結局
以下のような実装になりました。単純に読み込んだXMLファイルの文字列(タグ名、値)からクラスを生成する形をとろうと思います。
/**
* 各種設定ファイルを読み込むときに、ファイル名に対応した
* クラスを生成して返却する。
*
* @param fileName
* @return ConfigRpptを継承しているクラス
*/
public static ConfigIF createConfig(String fileName) {
ConfigIF conf = null;
switch (fileName) {
case StoryConfig.WORLD_XML:
conf = new World();
break;
case StoryConfig.NATURE_XML:
conf = new Nature();
break;
case StoryConfig.CREATURE_XML:
conf = new Creature();
break;
case StoryConfig.REGIONS_XML:
conf = new Region();
break;
case StoryConfig.EFFECTS_XML:
conf = new Effects();
break;
case StoryConfig.STM_XML:
conf = new STM();
break;
case StoryConfig.CPMMADS_XML:
conf = new Command();
break;
case StoryConfig.JOB_XML:
conf = new Job();
break;
case StoryConfig.MONSTER_TYPE_XML:
conf = new MonsterType();
case StoryConfig.MONSTERS_XML:
/** TODO-[MonsterクラスはPlayerクラスを継承する] */
//conf = new Monster();
break;
default:
System.out.println("想定外の設定ファイル名です。 : " + fileName);
System.exit(-1);
}
return conf;
}