JAXBを使ってXMLを読み取る
2023-02-26現在、テキストRPGを作成中です。
このゲームは、文字表現のみのRPGを作成できるアプリとして作成中です。
イメージとしては、ゲームブックをPC上で作成するようなものです。
以前作成したのは、制限が多く実行した後も見た目にカレントディレクトリが表示されたまま。。。など不適切なものが多かったので
javax.swingを使用して作り直している最中です。
XMLとは
「Extensible Markup Language」の略で、日本語では「拡張可能なマークアップ言語」
XMLファイルは、いろんな場所で使用されています。
-
ScenBuilder(JavaFX用の画面作成ツール)
-
SprintBoot: DI1コンテナの作成、MyBatisのようなORマッピング
-
クラスの読み込みとXML出力
2,3の項目には作成した動画がありませんでした。。。
JAXBとは
AXBとは、JavaでXMLを扱うためのライブラリの名称である(参照先)
作成したクラスをXMLファイルの形で出力、XMLファイルからクラスを取得と双方向で操作が可能。
<使用するライブラリ一覧>※gradleは使ってないので記載しません。。。
java9以降では、ライブラリをインポートしないといけないものがあるようです。
JAXBのAPIをインポートするのも大事ですが、ランタイムも行う必要があります。一番下の「jaxb-runtime」がそれにあたります。
<!-- JAXB -->
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- JAXB API only -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.0</version>
</dependency>
<!-- JAXB RI, Jakarta XML Binding -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>3.0.0</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.6</version>
</dependency>
出力するクラス(Worlds)
テキストRPGでの「世界」を保持するクラスWorldsクラスをJAXBで出力します。
世界クラスには、以下のようにフィールド変数が定義されています。そしてこれは、ユーザーがXMLファイルを定義するときに自由に記述できるものです。
つまり、ユーザーが世界クラスを生成する形をとっています。
下に記載しているのはWorldsクラスではなくWorldクラスです。
使用するクラス(アノテーション)
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement( name="worlds")
/**
* 世界を表すクラス。
* 基本的にゲーム作成者の指定する、設定情報を保持する。
*/
@Data
public class World extends StoryConfig {
/** img */
private String imgUrl;
/** 国 */
List<Country> countries;
/** 自然 */
private Nature nature;
/** 食物連鎖(説明、テキストファイルへのパス) */
private String food_chain;
/** 生物、Creture.xmlへのパス */
private List<Creature> creatures;
/** 地域 */
private List<Region> regions;
/** 魔法などの発動する仕組み、説明、テキストファイルへのパス */
private String logic;
/** 文明 */
private List<Civilization> civilizations;
/** 生活様式の説明、テキストファイルへのパス */
private String life_style;
/** 社会構造の説明、テキストファイルへのパス */
private String social_structure;
/** 組織の説明、テキストファイルへのパス */
private String organization;
/** 文化 */
private List<Culture> cultures;
public World() {
nature = new Nature();
regions = new ArrayList<>();
civilizations = new ArrayList<>();
cultures = new ArrayList<>();
creatures = new ArrayList<>();
countries = new ArrayList<>();
}
}
このクラスで、XMLで定義した内容「世界観」を保持します。
ゲームの最中で、この世界観を表示することができる形で実装したいと考えております。
※文字表現のみなので、そんなに難しくないと思っています。。。
出力したXMLファイル
上記のWorldクラスをリストにして持っているクラスWorldsクラスを出力しました。その結果です。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worlds>
<world>
<description>世界の説明</description>
<id>pppp</id>
<name>テスト用の世界</name>
<civilizations>
<description>文明1の説明</description>
<id>civil1</id>
<name>文明1</name>
<arts>文明1の芸術</arts>
<character>文明1の文字</character>
<structures>文明1の社会構造</structures>
<technology>文明1の技術</technology>
</civilizations>
<civilizations>
<description>文明2の説明</description>
<id>civil2</id>
<name>文明2</name>
<arts>文明2の芸術</arts>
<character>文明2の文字</character>
<structures>文明2の社会構造</structures>
<technology>文明2の技術</technology>
</civilizations>
<countries>
<description>A国の説明</description>
<id>country1</id>
<name>A国</name>
</countries>
<countries>
<description>B国の説明</description>
<id>country2</id>
<name>B国</name>
</countries>
<cultures>
<description>文化1の説明</description>
<id>culture1</id>
<name>文化1</name>
<habit>文化1の生活習慣</habit>
<life_style>文化1の生活スタイル</life_style>
<norm>文化1の規範</norm>
<organization>文化1の組織</organization>
<social_structure>文化1の社会構造</social_structure>
<values>文化1の価値観</values>
<view_of_world>文化1の世界観</view_of_world>
</cultures>
<nature>
<description>自然の説明</description>
<id>natureId</id>
<name>自然名</name>
<path></path>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<food_chain>食物連鎖</food_chain>
</nature>
</world>
<world>
<description>世界の説明</description>
<id>UpperWorld</id>
<name>上の世界</name>
<civilizations>
<description>文明1の説明</description>
<id>civil1</id>
<name>文明1</name>
<arts>文明1の芸術</arts>
<character>文明1の文字</character>
<structures>文明1の社会構造</structures>
<technology>文明1の技術</technology>
</civilizations>
<civilizations>
<description>文明2の説明</description>
<id>civil2</id>
<name>文明2</name>
<arts>文明2の芸術</arts>
<character>文明2の文字</character>
<structures>文明2の社会構造</structures>
<technology>文明2の技術</technology>
</civilizations>
<countries>
<description>A国の説明</description>
<id>country1</id>
<name>A国</name>
</countries>
<countries>
<description>B国の説明</description>
<id>country2</id>
<name>B国</name>
</countries>
<cultures>
<description>文化1の説明</description>
<id>culture1</id>
<name>文化1</name>
<habit>文化1の生活習慣</habit>
<life_style>文化1の生活スタイル</life_style>
<norm>文化1の規範</norm>
<organization>文化1の組織</organization>
<social_structure>文化1の社会構造</social_structure>
<values>文化1の価値観</values>
<view_of_world>文化1の世界観</view_of_world>
</cultures>
<nature>
<description>自然の説明</description>
<id>natureId</id>
<name>自然名</name>
<path></path>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<climateList>
<description>サンプル気候の説明</description>
<id>D</id>
<name>D気候</name>
</climateList>
<food_chain>食物連鎖</food_chain>
</nature>
</world>
</worlds>
余談
JAXBでXMLからクラスを生成するとき、対象のクラスには、デフォルトコンストラクタが定義されていないとエラーになる。
<XMLの一部抜粋>
<config>
<views>HP</views>
<views>MP</views>
<views>LV</views>
<money>
<key>NIG</key>
<name>ニギ</name>
<value>0</value>
</money>
<money>
<key>GLD</key>
<name>ゴールド</name>
<value>1</value>
</money>
<element>
<id>FIR</id>
<name>火</name>
</element>
<element>
<id>WIN</id>
<name>風</name>
</element>
<element>
<id>WAT</id>
<name>水</name>
</element>
<element>
<id>EAT</id>
<name>土</name>
</element>
</config>
<クラス>
package jp.zenryoku.rpg.data.config;
import lombok.Data;
/**
* このゲームで使用する要素・エレメントを定義する。
*
* @author (Takunoji)
* @version (1.0)
*/
@Data
public class Element
{
/** ID */
private String id;
/** 要素名 */
private String name;
/** デフォルトコンストラクタ */
public Element() {
}
public Element(String id, String name) {
this.id = id;
this.name = name;
}
}
出力する順序をつける
使用するクラス(アノテーション)
import javax.xml.bind.annotation.XmlType;
@XmlType(propOrder={"フィールド変数名1", "フィールド変数名2", "フィールド変数名3" ... })
出力したXML(ストーリーXML)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<story>
<description>はじめのタイトル表示を行いニューゲーム、コンテニューを選択、実行する</description>
<id>First</id>
<name>はじめの</name>
<path>config/stories/Story_0001.xml</path>
<sceneNo>0</sceneNo>
<sceneType>STORY</sceneType>
<nextScene>1</nextScene>
<canSelectNextScene>true</canSelectNextScene>
</story>
タグに属性をつける
Commandを指定するときに追加で属性をつけたくなり以下のように修正しました。
<commandStr kigo="ATK" level="1"/>
「レベル」という小目を追加しました。これは、このコマンドがレベルなんぼ?の時に使用可能か?を定義するためのものです。
これをJavaで表現するためには、以下のようにクラスを追加してやる必要がありました。
@XmlAccessorType(XmlAccessType.FIELD)
public class CommandStr {
@XmlAttribute
private String kigo;
@XmlAttribute
private int level;
public CommandStr() {
}
public CommandStr(int level, String kigo) {
this.level = level;
this.kigo = kigo;
}
}
JobクラスのcommandStrに追加する情報でした。
@Data
public class Job extends StoryConfig {
private List<CommandStr> commandStr;
private int level;
private List<Command> commandList;
private List<Params> paramList;
}
こんな感じです。これを読み込んでゲームを開始するというようなアプリがテキストRPGなのですが、まだまだ問題はヤマズミです。。。