libgdxのクラスでjsonを使う

今作ってるゲームで、データを外部ファイル化させたいなあ。となった。 xmlでもいいんだけど、今回はjsonで。

jsonといえば、jacksonが有名なのかな、と思いますがlibgdxにもjsonを扱うクラスがあります。

新たなjarを追加しなくていいですしあまり難しいことをしないならこちらでも十分だと思います。

前提

・公式wikiのここ↓の内容です。

https://github.com/libgdx/libgdx/wiki/Reading-%26-writing-JSON

・注意:今作っているゲームは、不思議なダンジョンっぽいゲームなこともあって、 ダンジョンの情報を外で持っておきたいという趣旨なので、コードもそれっぽいです。

javaクラス

public class DungeonDataList {
    public Array<Dungeon> dungeons;  //全ダンジョン情報
}

class Dungeon{
    char id;   //ダンジョンのID
    protected String name; //ダンジョンの名前
    private int floorNo = 0; //ダンジョンのフロア数

    @Override
    public String toString() {
        return "Dungeon [id=" + id + ", name=" + name + ", floorNo=" + floorNo + "]";
    }

    public void setFloorNo(int floorNo) {
        this.floorNo = floorNo;
    }
}

ダンジョンの情報(ダンジョンの名前と、ダンジョンのフロア数) = Dungeonクラス

全てのダンジョンの情報(Dungeonクラス)を配列でもったDungeonDataを用意しておきました。

Javaオブジェクト→JSON文字列

Json#toJson で変換するオブジェクトと、変換する型を指定。

public class ObjectToJsonSampleListener extends ApplicationAdapter {
    @Override
    public void create() {
        DungeonDataList obj = new DungeonDataList();
        obj.dungeons = new Array<>();
        Dungeon dungeon1 = new Dungeon();
        dungeon1.id = 'A';
        dungeon1.name = "Home";  //おうちダンジョン
        dungeon1.setFloorNo(5);
        obj.dungeons.add(dungeon1);

        Dungeon dungeon2 = new Dungeon();
        dungeon2.id = 'B';
        dungeon2.name = "Forest";    //森ダンジョン
        dungeon2.setFloorNo(3);
        obj.dungeons.add(dungeon2);

        Json json = new Json();
        String jsonText = json.toJson(obj, DungeonDataList.class );
        jsonText = json.prettyPrint( jsonText, setting);
        System.out.println( jsonText );
    }
}

・実行結果

{dungeons:[{id:A,name:Home,floorNo:5},{id:B,name:Forest,floorNo:3}]}

JSON文字列→Javaオブジェクト

Json#fromJson に変換する型とJSON文字列を指定。

この例では、jsonファイルを読んで*1、その文字列をオブジェクトに変換します。

public class JsonToObjectSampleListener extends ApplicationAdapter {

    @Override
    public void create() {
        String jsonText = Gdx.files.internal( "dungeon.json" ).readString();
        Json json = new Json();
        DungeonDataList obj = json.fromJson( DungeonDataList.class, jsonText );

        System.out.println("dungeon1: " + obj.dungeons.get(0));
        System.out.println("dungeon2: " + obj.dungeons.get(1));
    }
}

・出力結果

dungeon1: Dungeon [id=A, name=Home, floorNo=5]
dungeon2: Dungeon [id=B, name=Forest, floorNo=3]

型引数を持ったArray,String,int, char を柔軟に変換できています。

また、フィールドのスコープも関係なく(getter/setterを作成しなくても)変換できていることがわかります。

入出力の設定

public class ObjectToJsonSampleListener extends ApplicationAdapter {
    @Override
    public void create() {
        //〜データ詰める部分省略

        Json json = new Json();
        String jsonText = json.toJson(obj, DungeonData.class );
        
        JsonValue.PrettyPrintSettings setting = new JsonValue.PrettyPrintSettings();
        setting.outputType = JsonWriter.OutputType.json;
        jsonText = json.prettyPrint( jsonText, setting);
        System.out.println( jsonText );
    }
}

・実行結果

{
"dungeons": [
    {
        "id": "A",
        "name": "Home",
        "floorNo": 5
    },
    {
        "id": "B",
        "name": "Forest",
        "floorNo": 3
    }
]
}

Json#prettyPrint でjson文字列に変換する際に、インデントなど整形してくれます。

JsonValue.PrettyPrintSettings#outputType で任意の形式(上の例はjson)に指定することで(これをやらないと"がなかったりします)、 整形されたJSON文字列が出力されます。

型が不明確なとき

public Array<Dungeon> dungeons; //全ダンジョン情報

これまでの例では、リスト(Array)クラスのフィールドが、Dungeonクラスだと明確にジェネリクスで宣言していました。

ですが、これでは同じようなことをするクラスが出てきた場合に非常に不便です。idやnameはダンジョンの情報でなくても持っていることも多いと思います。

public Array<T extends BaseData> dungeons; 

なのでBaseDataというスーパークラスを用意しておいて、条件つきのジェネリクスを定義するとします。

この場合、型が明確ではない(BaseDataを継承するなんらかの子クラス)ため、JsonValueオブジェクトとして扱われます。

※型を指定しない場合も同じだった

json文字列をどの型にマッピングするかを指定したい場合は、Json#setElementTypeで指定することができます。

json.setElementType(BaseDataList.class, "dungeons", Dungeon2.class);

基本的なことはここまで。このあとは必要が出てきたら追記