.dfmをYAMLのように使う
Delphiでは、RTTIを用いたストリーミングで、.dfm形式については比較的簡単に読み書きすることができます。.dfmの実体はWindowsのリソースファイルですので、実行ファイルにリンクすることもでき、実際にVCLでは、フォームエディタで設計時に配置したコンポーネント群をリソースとして、実行時に復元することでRADを実現しています。
Delphi限定ですが、.dfmというのは便利なフォーマットですので、汎用のデータファイルとしても色々使いたいのです。
汎用のデータファイルについてはXMLやYAMLやJSON等がありますが、これらはそれぞれに長所と短所があります。主に好みのレベルで。私はこの中ではYAMLが好きです。
そこで、.dfmとYAMLの間の対応表を作って幸せになろうとそんな話です。
.dfmはバイナリフォーマットですが、幸いテキストでの表現も用意されており、以下ではテキストでの表現を使用しています。DelphiのIDEで開く/付属のconvert.exeで変換する/その辺に転がってるツールを使う、等でテキスト表現を見ることができます。
map (outside)
--- !TypeName ...
object TypeName ... end
YAMLとしてはlistとmapのどちらからでも開始できますが、.dfm形式からくる制約により最外周は必ずmapになります。
map
--- !TypeName A: 1 B: 3.14 C: "string" D: SYMBOL ...
object TypeName A = 1 B = 3.14 C = 'string' D = SYMBOL ... end
各型のプロパティとして対応付けます。
map (in map)
--- !TypeName E: A: 1 B: 3.14 ... ...
object TypeName E.A = 1 E.B = 3.14 E. ... ... end
TPersistent派生のプロパティとして表現できますが、やや冗長です。
--- !TypeName &E E: !TypeOfE A: 1 B: 3.14 ... ...
object TypeName object E: TypeOfE A = 1 B = 3.14 ... end ... end
TComponent派生のプロパティとしても表現できますが、型名が必要となりますし、アンカーとしても機能してしまうため名前が重複できなくなります。逆に言えばmap以外はアンカーを付けられません。
どうでもいいですがYAMLのReference Parser、タグとアンカーが続く場合をパースできないような……。
↓エラーになる。
--- !TypeName &E E: !TypeOfE A: 1 B: 3.14
↓パースできる。
--- !TypeName E: !TypeOfE A: 1 B: 3.14
--- &E E: !TypeOfE A: 1 B: 3.14
--- !TypeName A: 1 &E E: !TypeOfE A: 1 B: 3.14
list
--- !TypeName F: - 1 - 3.14 ... ...
object TypeName F = (1 3.14 ...) ... end
スカラー値のlistは、.dfmでもリスト(vaList)として表現します。直接対応するPascalの型はありませんので、読み書きのためにはDefinePropertiesをoverrideする必要があります。場合によっては集合型も使えます(括弧が[ ]になります)。
map (in list)
--- !TypeName G: - A: 1 B: 3.14 ... - A: 2 B: 1.41 ... ... ...
object TypeName G = < item A = 1 B = 3.14 ... end item A = 2 B = 1.41 ... end ...> ... end
mapのlistは、TCollection派生のプロパティとして表現します。
list (in list)
--- !TypeName H: - - 1 - 3.14 ... - - 2 - 1.41 ... ... ...
object TypeName G = ((1 3.14 ...) (2 1.41 ...) ...) ... end
Pascalの型として集合の集合はありませんが、.dfmの表現上は可能です。読み書きはDefinePropertiesをoverrideする必要があります。
map (in list in list)
--- !TypeName I: - - A: 1 B: 3.14 ... - A: 2 B: 1.41 ... ... - - A: 3 B: 1.73 ... ... ... ...
object TypeName I = ( < item A = 1 B = 3.14 ... end item A = 2 B = 1.41 ... end ...> < item A = 3 B = 1.73 ... end ...> ...) ... end
TCollection派生の更に集合として表現します。
まとめ
YAML | .dfm |
list (of map) | TCollection |
list | set |
map (in map) | TPersistent / TComponent |
map (in list) | TCollectionItem |
map (outside) | TComponent |