たくのろじぃのメモ部屋

プログラミング関係や数学、物理学などの内容を備忘録として残すブログ。

【C#】JSON ファイルを書きだす

去年、C#JSONの読み込み(デシリアライズ)をやりました。

blog.takunology.jp

今回は書き出し(シリアライズ)をやります。と言っても、シンプルなJSONファイル(例えばオブジェクトや配列が1つの階層のもの)はいろんなサイトに載っているので、このブログでは多重構造になっているものを扱います。

書き出したいデータ

統合版マインクラフトのWebSocket通信で使用するJSONファイルを書き出してみます。

{
    "body": {
        "commandLine": "time set 0",
        "origin": {
            "type": "player"
        },
        "version": "1"
    },
    "header": {
        "messagePurpose": "commandRequest",
        "messageType": "commandRequest",
        "requestId": "e608eba7-13cb-48f9-b8d5-139088cc7cd8",
        "version": "1"
    }
}

これの面倒なところは body オブジェクトの中に origin オブジェクトが入っている点です。この二重構造になっているものをどうやって書き出すかがポイントです。

1. データコントラクト属性クラス

まずは書き出したいデータに対応するクラスを作ります。その際に、データコントラクト属性を付けます。クラス名は SampleData としています。オブジェクトの中にオブジェクトが入るような構造をしている場合は、クラスのクラス(おそらくネスト?)を作っていきます。例えば Body クラスには Origin クラスでネストしたプロパティが含まれています。

各プロパティにはデータメンバ属性を付けて、JSONに書き出す際の Key とします。Name プロパティを指定することで Key の名前を変更できます。

namespace JsonSerializeSample
{
    [DataContract]
    public class SampleData
    {
        [DataMember(Name = "body")]
        public Body Body { get; set; }
        [DataMember(Name = "header")]
        public Header Header { get; set; }
    }

    [DataContract]
    public class Origin
    {
        [DataMember(Name = "type")]
        public string type { get; set; }
    }

    [DataContract]
    public class Body
    {
        [DataMember(Name = "origin")]
        public Origin Origin { get; set; }

        [DataMember(Name = "commandLine")]
        public string commandLine { get; set; }

        [DataMember(Name = "version")]
        public string version { get; set; }
    }

    [DataContract]
    public class Header
    {
        [DataMember(Name = "requestId")]
        public string requestId { get; set; }

        [DataMember(Name = "messagePurpose")]
        public string messagePurpose { get; set; }

        [DataMember(Name = "version")]
        public string version { get; set; }

        [DataMember(Name = "messageType")]
        public string messageType { get; set; }
    }
}

2. シリアライズ

定義したプロパティを初期化した後、シリアライズをするためのインスタンスを生成します。また、メモリストリームでファイルの順次書き出しを行えるようにします。シリアライズインスタンスにて WriteObject メソッドを呼び出して、引数にメモリストリーム(順次書き出し)と初期化したデータを入れます。

Body クラスの中に Origin クラス(内部クラス)が宣言されているのがポイントです。これで二重構造にできます。

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;

namespace JsonSerializeSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Json serialize.");
            Serialize();
        }

        static void Serialize()
        {
            var sampleData = new SampleData()
            {
                Body = new Body()
                {
                    Origin = new Origin() { type = "player" },
                    commandLine = "time set 0",
                    version = "1"
                },

                Header = new Header()
                {
                    requestId = "e608eba7-13cb-48f9-b8d5-139088cc7cd8",
                    messagePurpose = "commandRequest",
                    version = "1",
                    messageType = "commandRequest"
                }
            };

            var serializer = new DataContractJsonSerializer(typeof(SampleData));
            var ms = new MemoryStream();
            serializer.WriteObject(ms, sampleData);
            Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
        }
    }
}

3. 実行結果

画像では改行されずに出力されていますが、JSON形式で書き出せています。 生のデータでは少しわかりにくいので、整形ツールなどを使うと見やすくなります。

f:id:takunology:20201009143019p:plain

参考

今回作ったコードを置いておきます。動かしたい人は .NET Core 3.1 以上で動かしてください。

github.com