CSV, TSV を Serialize/Deserialize する Unity 向けライブラリとして XsvUtility
を作りました。
概要
XsvUtility は XSV *1 と任意の型とを相互に変換するためのライブラリです。
C# の属性をクラス・構造体のフィールド・プロパティに付与するコトで柔軟性の高い Serialize/Deserialize を実現しています。
何故作ったの?
キッカケとしては、以下のような流れ。
- 「AssetBundle のサイズ情報を記録したファイルを Amazon S3 にでも置いて管理しようかな。」
- 「JSON でも良いんだけど、CSV とか TSV のが楽なんだよなぁ…。」
- 「でもパーサとか無いんだよなぁ…。」
- 「作ろう。」
- 「ついでだから Unity Package Manager で配信できるようにしてみよう。」
思いっきり車輪を再発明している気がしないでも無いですが、まぁ勉強目的ってコトで。
特徴
JsonUtility
ライクな使い心地
UnityEngine.JsonUtility
は FromJson<T>()
や ToJson()
によって、 Serializable なクラス・構造体の Serializable なフィールドの値を読み書き できるわけですが、 XsvUtility
は XSV をパースした結果の行・列を、特殊な C# 属性を付けたフィールド・プロパティに読み書きする 機能を Deserialize<T>()
や Serialize()
というメソッドによって提供します。
リポジトリの README にも書いてありますが、以下のような感じです。
構造の宣言
class Foo { [XsvRow] public IEnumerable<Bar> Bars { get; set; } // List<T> などの IEnumerable な Generics 型であれば何でも OK } struct Bar { [XsvColumn(0)] public string Baz; [XsvColumn(1)] public int Quz { get; set; } }
Deserialize/Serialize
var csvText = @"aaa,100 bbb,200"; var foo = Monry.XsvUtility.CsvSerializer.Deserialize<Foo>(csvText); foo.Bars.ToList()[0].Baz; // aaa foo.Bars.ToList()[1].Quz; // 200
var foo = new Foo { Bars = new List<Bar> { new Bar {Baz = "aaa", Quz = 100}, new Bar {Baz = "bbb", Quz = 200}, } }; var tsvText = Monry.XsvUtility.TsvSerializer.Serialize(foo); // aaa,100\nbbb,200
プロパティ・フィールドの両方に対応
Unity のシリアライザは仕様上、 [SerializeField]
を付けたフィールドか public
なフィールド のみをシリアライズ対象としています。
XsvUtility では、範囲を拡大して [XsvColumn]
を付けたフィールドかプロパティ をサポートしています。
アクセス可視性についても、 private
もシリアライズ対象としているので、既存のクラス・構造体を汚さないんじゃないかと思っています。
各行をパースした結果を保持するためには [XsvRow]
を付けた IEnumerable<T>
なフィールド・プロパティ を利用するようにしています。
また、現時点では IEnumerable<T>
の <T>
の型に対して [Serializable]
なクラス・構造体であることを強制していません。
これはいずれ仕様を変える可能性はあります。
特殊な XSV のパースに対応
Microsoft Office などで CSV を出力する際にカンマや改行を含むセルは "
で囲まれるという仕様や、ダブルクォーテーションで囲まれているセル内のダブルクォーテーションは ""
という風に重ねるという仕様があり、XsvUtility はそれをサポートしています。
詳しくはリポジトリに含めているテストを参照してください。
Unity Package Manager (upm) に対応
まだ完全民主化されていない Unity Package Manager ではありますが、依存を持たないパッケージであれば GitHub の URL を指定出来るようになっています。
ということで、 upm をサポートしてみました。
upm の仕様で、 package.json
をリポジトリルートに置かなければいけないため、 upm 用のリポジトリを別に分けて、開発用のリポジトリから submodule という形で upm 用のリポジトリ を参照させることにしました。*2
まだ author
が表示されなかったり、category
の使いどころが分からなかったりと、色々ありますが、今後の民主化に期待しましょう。
課題
Documentation, ChangeLog
upm 的にこの辺のルールがあるっぽいので、対応してみる所存です。
なんか、今の時点では Package Manager UI のリンクをクリックしても https://docs.unity3d.com/Packages/tv.monry.xsvutility@0.1/index.html
的な URL に飛ばされてしまうので、どういう形でサポートされるのかは未知数ですが。
パフォーマンス
リフレクションをガッツリ使っていたり、あまり最適化ができていなかったりするので、パフォーマンスはそんなに良くないと思います。
まぁ、そもそも CSV やら TSV やらは構造上最適化が難しい気もするので、あくまでサッと利用する目的ってコトで。
カリカリにチューニングしたぜ!って人からの PullRequest 受付中ですよ!w