TJsonSerializerの実用例
TFormなどを継承したクラスの場合
例えば起動時と終了時にフォームの位置を記憶・復元することを考えた場合、TJsonSerializerにTForm1などのインスタンスを渡すことになりますが、デフォルトでは余計なデータまで保存されてしまうため、保存項目を自分で指定するためにまずTForm1にJsonSerialize(TJsonMemberSerialization.In)というカスタム属性を指定し、次に保存したいメンバにJsonIn属性を持たせることで任意の項目のみをシリアライズすることになります。
しかし自分で定義した型と違い、TFormの派生クラスでは親クラスより上の段階でLeftやTopといったプロパティが定義されているため、ぱっと見属性を書く場所がありません。そういった場合はプロパティを再定義し、そこに属性を記述します。
type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); published [JsonIn] property Left; // <==再定義 [JsonIn] property Top; // <==再定義 end; procedure TForm1.FormCreate(Sender: TObject); var serializer: TJsonSerializer; begin if FileExists('config.json') then begin serializer := TJsonSerializer.Create; try serializer.Populate(TFile.ReadAllText('config.json', TEncoding.UTF8), Self); finally serializer.Free; end; end; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var serializer: TJsonSerializer; begin serializer := TJsonSerializer.Create; try TFile.WriteAllText('config.json', serializer.Serialize(Self), TEncoding.UTF8); finally serializer.Free; end; end;
ちなみに属性を何も指定せずにTFormのインスタンスをシリアライズしようとすると、スタックオーバーフローが発生します。これはTFormのプロパティを辿っていくと再び自身(TForm)が出てくる場合があり、無限ループになってしまうせいです。そのため必ず属性による制御が必要です。
定義済みの型の一部のみをシリアライズしたい場合
先の例は継承したクラスがあるため自分で属性付けが可能でしたが、継承せずそのまま使うクラスであったり、そもそも継承が不可能なレコードの場合、直接カスタム属性を付与することはできません。この場合は少し面倒になりますが、TJsonDynamicContractResolverを使用して動的に属性を付与することになります。
例えばTRectをシリアライズしようと考えた場合、そのままシリアライズするとLeft, Top, Right, Bottomだけではなく、可変部分であるTopLeftとBottomRightも一緒に出力されてしまいます。そこでLeft, Top, Right, Bottomのみをシリアライズするような場合を考えます。
var rect: TRect; serializer: TJsonSerializer; resolver: TJsonDynamicContractResolver; begin rect.Left := 100; rect.Top := 200; rect.Right := 300; rect.Bottom := 400; serializer := TJsonSerializer.Create; try resolver := TJsonDynamicContractResolver.Create; serializer.ContractResolver := resolver; resolver.SetFieldsIgnored(TypeInfo(TRect), ['TopLeft', 'BottomRight']); TFile.WriteAllText('rect.json', serializer.Serialize(rect), TEncoding.UTF8); finally serializer.Free; end; end;
何も指定せずにシリアライズすると{"Left":100,"Top":200,"Right":300,"Bottom":400,"TopLeft":{"X":100,"Y":200},"BottomRight":{"X":300,"Y":400}}となってしまいますが、このコードでシリアライズすると{"Left":100,"Top":200,"Right":300,"Bottom":400}のみが得られます。
なお、ContractResolverプロパティはインターフェース型で、代入した時点で自動管理になるのでresolverを自身で解放する必要はありません。