もくじ
目次(WPF/xaml/C#/C++関連メモ) - tera1707’s blog
やりたいこと
jsonファイルを読み込むときに、以前調べたDataContractJsonSerializerクラス
をよく使うがjsonを読み込んだときにデータを保存するためのクラスをいちいち書くのがめんどくさい。あと長い。なにか短くかける方法がないか調べたい。
やったこと
C #9.0
で追加されたrecord型
を使う。
前提
下記で実験。
VisualStudio2022/.NET6/C#10
コード
classでつくる
以前DataContractJsonSerializer
の実験で作ったjsonデータを格納するためのクラスは、下記の通り。
public class Rootobject { public int data1 { get; set; } public string data2 { get; set; } public bool data3 { get; set; } public Arraydata[] arraydata { get; set; } } public class Arraydata { public int data11 { get; set; } public float[] small { get; set; } public float[] large { get; set; } }
recordでつくる
これと同じものをrecord
で作ると下記のようになる。
こうすれば、前回作ったジェネリクスでjsonを読み込むコードも、そのまま使える。
[DataContract] record Rootobject( [property: DataMember] int data1, [property: DataMember] string data2, [property: DataMember] bool data3, [property: DataMember] Arraydata[] arraydata ); [DataContract] record Arraydata( [property: DataMember] int data11, [property: DataMember] float[] small, [property: DataMember] float[] large );
注意
試した限り、classだと、[DataContract]のアトリビュートをつけなくてもうまく動いたが、recordだとそれらを付けてやらないとdata = (T?)serializer.ReadObject(stream);
でデシリアライズするときに例外が起きた。([property: DataMember]
は、つけなくても動くのは動いた。)
注意点
但し、このままだと、例えば
ということができない。
これは、上のようにrecordでRootobjectを作成すると、data1
について、コンパイラが裏で
[property: DataMember] public int data1 { get; init; }
のようにしてくれていて、data1の値をコンストラクタでしか入れることができないようになっているため。 これを、
[DataContract] record Rootobject( [property: DataMember] string data2, [property: DataMember] bool data3, [property: DataMember] Arraydata[] arraydata) { [property: DataMember] public int data1 { get; set; } }
と書いてあげると、data1だけ編集することができるようにできるが、 そういうややこしいことをするなら、全部classで作った方がよいような気がする。
考察
「短くかけたかどうか?」という視点だけだと「{get;set}がなくなったくらい?」と思ってしまうが、 こちらのページにあるように、「jsonのデータ」を扱うためのものとしてこのrecordを見ると、比較したりするうえではrecordの方が、だいぶ扱いやすくなっていると思った。
追記 プライマリコンストラクタを使わなければ、classと同じような書き方でrecordが使える
上のように、プライマリコンストラクタ("()"の中にプロパティを並べて書くやつ)を使わずに、jsonで使いたいとき(≒値を編集したいとき)はクラスと同じ書き方で書けば、値を書き換えることができつつ、recordの便利さを生かしつつ、見た目にもわかりやすい(classと書き方が同じという意味で)ようにかける。
public record Rootobject() { public int Data1 { get; set; } public string? Data2 { get; set; } public bool Data3 { get; set; } public Arraydata[]? Arraydata { get; set; } } public record Arraydata { public int Data11 { get; set; } public float[]? Small { get; set; } public float[]? Large { get; set; } }
※難しく考えすぎていたかも。
参考
record型の詳しい説明。 ここを見ればすべてわかる。 ここの「プライマリ コンストラクター引数への属性付与」の項目に、Attributeの付け方が詳しく書かれている。
レコードのプロパティへのアトリビュートの設定の仕方
レコードのプロパティへのアトリビュートの設定の仕方2
レコードのプロパティへのアトリビュートの設定の仕方3