2018年5月3日木曜日

TokyoでJson形式のファイルの読み書きをして見た。

唐突ですが、Json.NET良いですよね。高度なこともできますが、Json形式の文字列のシリアル化/デシリアライズ化がだけで良いな簡単にできますしね。
Delphi(Tokyo)でJson形式のファイルを読み込む必要が出てきたので、Json.NETのような書き方ができなか調べていたところもう1年くらい前の記事になりますが、

@lynatan さんのTJsonSerializerの記事

TJsonSerializerの使い方。
TJsonSerializerの実用例

エンバカデロさんのブログ

TJsonSerializerでJSONに変換する[JAPAN]

DelphiでもTokyoになって、Json.NETのような書き方ができるようになっているとのことでしたので、試して見ました。

読み書きするのは、以下のような内容のファイル

{"ShopName":"ラビットハウス",
  "Clerks":[
    {"Name":"ココア","Age":17},
    {"Name":"チノ","Age":15},
    {"Name":"リゼ","Age":17}
  ]
}

上記の構造にマップできるクラスを作成します。
先ずは、従業員(Clerk)クラス。
パブリックメンバーをシリアライズ対象のするのでクラスに[JsonSerialize(TJsonMemberSerialization.&Public)]
属性を付加しています。
(余談ですが、予約語、指令と被るワードを使う場合、その前に"&"が必要です。)

  1. unit Unit1;  
  2.   //パブリックメンバーをシリアライズ対象にする属性  
  3.   [JsonSerialize(TJsonMemberSerialization.&Public)]  
  4.   TClerk = class  
  5.     private  
  6.       FName: string;  
  7.       FAge: integer;  
  8.   
  9.       procedure SetAge(const Value: integer);  
  10.       procedure SetName(const Value: string);  
  11.     public  
  12.       property Name : string read FName write SetName;  
  13.       property Age : integer read FAge write SetAge;  
  14.       constructor Create; overload;  
  15.       constructor Create(const vName : stringconst vAge : integer); overload;  
  16.   end;  

次に喫茶店(CoffeeShop)クラスの定義
  1. [JsonSerialize(TJsonMemberSerialization.&Public)]  
  2. TCoffeeShop = class  
  3.   private  
  4.   FClerks: TList<TClerk>;  
  5.   FShopName: string;  
  6.   procedure SetClerks(const Value: TList<TClerk>);  
  7.   procedure SetShopName(const Value: string);  
  8.   public  
  9.     property ShopName : string read FShopName write SetShopName;  
  10.       
  11.     //TClerkクラスのジェネリックリスト用のコンバーターを登録  
  12.     [JsonConverter(TJsonClerkListConverter)]  
  13.     property Clerks : TList<TClerk> read FClerks write SetClerks;  
  14.   
  15.     public constructor Create;  
  16. end;  

メンバーは、喫茶店名と、従業員のリスト(ジェネリックのリスト)です。
こちらも、パブリックメンバーをシリアル化の対象とします。
ジェネリックのリストは、そのままではシリアライズできないので、Json.Converterユニットに定義済みの TJsonListConverterからTClerk型用の派生クラス

  1. //TClerk型のリスト用のコンバーター  
  2. TJsonClerkListConverter = class(TJsonListConverter<TClerk>);  

を作成し、TClerk型のジェネリックリスト型のメンバーClerks用のコンバーター属性を付加しています。

クラス定義の全体は、以下とおりです。
  1. unit Unit2;  
  2.   
  3. interface  
  4. uses  
  5.   //Json.SerializersとConverterを使用する。  
  6.     System.JSON.Serializers  
  7.   , System.JSON.Converters  
  8.   //TList<t>を使用する  
  9.   , System.Generics.Collections  
  10.   ;  
  11.   
  12. type  
  13.   
  14.   //パブリックメンバーをシリアライズ対象にする属性  
  15.   [JsonSerialize(TJsonMemberSerialization.&Public)]  
  16.   TClerk = class  
  17.     private  
  18.       FName: string;  
  19.       FAge: integer;  
  20.   
  21.       procedure SetAge(const Value: integer);  
  22.       procedure SetName(const Value: string);  
  23.     public  
  24.       property Name : string read FName write SetName;  
  25.       property Age : integer read FAge write SetAge;  
  26.       constructor Create; overload;  
  27.       constructor Create(const vName : stringconst vAge : integer); overload;  
  28.   end;  
  29.   
  30.   //TClerk型のリスト用のコンバーター  
  31.   TJsonClerkListConverter = class(TJsonListConverter<TClerk>;);  
  32.   
  33.   [JsonSerialize(TJsonMemberSerialization.&Public)]  
  34.   TCoffeeShop = class  
  35.     private  
  36.     FClerks: TList<TClerk>;  
  37.     FShopName: string;  
  38.     procedure SetClerks(const Value: TList<TClerk>);  
  39.     procedure SetShopName(const Value: string);  
  40.     public  
  41.       property ShopName : string read FShopName write SetShopName;  
  42.         
  43.       //TClerkクラスのジェネリックリスト用のコンバーターを登録  
  44.       [JsonConverter(TJsonClerkListConverter)]  
  45.       property Clerks : TList<TClerk> read FClerks write SetClerks;  
  46.   
  47.       public constructor Create;  
  48.   end;  
  49. implementation  
  50.   
  51. { TClerk }  
  52.   
  53. constructor TClerk.Create(const vName: stringconst vAge: integer);  
  54. begin  
  55.    FName := vName;  
  56.    FAge := vAge;  
  57. end;  
  58.   
  59. constructor TClerk.Create;  
  60. begin  
  61.   
  62. end;  
  63.   
  64. procedure TClerk.SetAge(const Value: integer);  
  65. begin  
  66.   FAge := Value;  
  67. end;  
  68.   
  69. procedure TClerk.SetName(const Value: string);  
  70. begin  
  71.   FName := Value;  
  72. end;  
  73.   
  74. { TCoffeeShop }  
  75.   
  76. constructor TCoffeeShop.Create;  
  77. begin  
  78.   FClerks := TList<TClerk>.Create;  
  79. end;  
  80.   
  81. procedure TCoffeeShop.SetClerks(const Value: TList<TClerk>);  
  82. begin  
  83.   FClerks := Value;  
  84. end;  
  85.   
  86. procedure TCoffeeShop.SetShopName(const Value: string);  
  87. begin  
  88.   FShopName := Value;  
  89. end;  
  90.   
  91. end.  

上記で定義したクラスに対で、冒頭で示した内容のJson形式の定義ファイルCoffeeShop.Jsonを読み込み、デシリアライズする処理は、以下のとおりとなります。

  1. procedure TForm1.Button2Click(Sender: TObject);  
  2. var  
  3.   CoffeeShop : TCoffeeShop;  
  4.   serializer: TJsonSerializer;  
  5.   s : string;  
  6.   Clerk : TClerk;  
  7. begin  
  8.   
  9.   s := TFile.ReadAllText('CoffeeShop.Json',TEncoding.UTF8);  
  10.   
  11.   serializer := TJsonSerializer.Create;  
  12.   try  
  13.     CoffeeShop := serializer.Deserialize<TCoffeeShop>(s);  
  14.     try  
  15.       Memo1.Clear;  
  16.       Memo1.Lines.Add(CoffeeShop.ShopName);  
  17.       for Clerk in CoffeeShop.Clerks do  
  18.       begin  
  19.         Memo1.Lines.Add(Clerk.Name + '(' + Clerk.Age.ToString() + ')');  
  20.       end;  
  21.     finally  
  22.       CoffeeShop.Clerks.Clear();  
  23.       CoffeeShop.Free;  
  24.     end;  
  25.   finally  
  26.     serializer.Free  
  27.   end;  

ファイルを読み込み、シリアライザーを生成し、読み込んだJson文字列をデシリアライズして、ショップ名と、店員の情報をメモに表示しています。 (余談ですが、クラス定義で属性を使用しないで、TJsonSerializerインスタンスのConverterリストにTJsonClerkListConverterのインスタンスを登録してもデシリアライズできます。)

冒頭で示した内容のJson形式の定義ファイルを作成する場合は、以下の処理でできます。

  1. procedure TForm1.Button1Click(Sender: TObject);  
  2. var  
  3.   CoffeeShop : TCoffeeShop;  
  4.   serializer: TJsonSerializer;  
  5.   s : string;  
  6.   
  7.   
  8. begin  
  9.   CoffeeShop := TCoffeeShop.Create;  
  10.   try  
  11.     CoffeeShop.ShopName := 'ラビットハウス';  
  12.     CoffeeShop.Clerks.Add(TClerk.Create('ココア',17));  
  13.     CoffeeShop.Clerks.Add(TClerk.Create('チノ',15));  
  14.     CoffeeShop.Clerks.Add(TClerk.Create('リゼ',17));  
  15.   
  16.     serializer := TJsonSerializer.Create;  
  17.     try  
  18.   
  19.       s := serializer.Serialize(CoffeeShop);  
  20.       Memo1.Text := s;  
  21.   
  22.       TFile.WriteAllText('CoffeeShop.Json',s);  
  23.   
  24.     finally  
  25.       serializer.Free;  
  26.     end;  
  27.   
  28.   finally  
  29.     CoffeeShop.Clerks.Clear();  
  30.     CoffeeShop.Free;  
  31.   end;  
  32. end;  

TCoffeeShop型のインスタンスを生成し、メンバーを設定後、シリアライザーを生成しシリアライズ後、ファイルに保存しています。
(確認の為、画面上のメモにも表示しています。)


プロジェクト一式は、https://bitbucket.org/OldTPFun/delphitest/src/master/JsonTest/Proj1/
に配置しております。

0 件のコメント: