😒

C#: 匿名型とValueTupleではシリアライズ結果が違う

2021/10/27に公開1

入力例

匿名型

var rect1 = new
{
    left = 1,
    top = 2,
    right = 3,
    bottom = 4,
};

ValueTuple

var rect2 = 
(
    left: 1,
    top: 2,
    right: 3,
    bottom: 4
);

MessagePack-CSharp

MessagePack-CSharp 2.3.85 を使用してMessagePack形式にする場合、以下のような差になります。

using MessagePack;

var msgpack1 = MessagePackSerializer.Serialize(rect1);
var msgpack2 = MessagePackSerializer.Serialize(rect2);

Console.WriteLine(MessagePackSerializer.ConvertToJson(msgpack1));
Console.WriteLine(MessagePackSerializer.ConvertToJson(msgpack2));
{"left":1,"top":2,"right":3,"bottom":4}
[1,2,3,4]

Json.NET

Json.NET (Newtonsoft.Json) 13.0.1 を使用した場合は以下の通りでした。

using Newtonsoft.Json;

Console.WriteLine(JsonConvert.SerializeObject(rect1));
Console.WriteLine(JsonConvert.SerializeObject(rect2));
{"left":1,"top":2,"right":3,"bottom":4}
{"Item1":1,"Item2":2,"Item3":3,"Item4":4}

System.Text.Json

.NET 5にてSystem.Text.Json を使用した場合は以下の通りでした。

using System.Text.Json;

Console.WriteLine(JsonSerializer.Serialize(rect1));
Console.WriteLine(JsonSerializer.Serialize(rect2));
{"left":1,"top":2,"right":3,"bottom":4}
{}

まとめ

あるモデルの値をリテラルのみによってC#ソースコード中に書きたいと思ったとき、昔からある匿名型に加え、最近ValueTupleによる記法もでき、この2択になりました。ただし、両者はC#における触り心地はそれほど大差なくても、シリアライズすると結構差がありました。概ね、匿名型の方が芳しい結果に思えます。

おことわり

各種オプションはあまり調べておらず、各ライブラリで最も素朴な使い方のみ試しています。別な結果にできる可能性はあります。

コード

.NET Fiddleですぐ動作も確認できます。https://dotnetfiddle.net/g2UtPA

Discussion

soi013soi013

System.Text.JsonでValueTupleがシリアライズされないのは、中の値がフィールドだからです。
これはIncludeFieldsを指定することで回避できます。

System.Text.Json.JsonSerializerOptions option = new()
{               
    IncludeFields = true,
};

Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(rect1, option));
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(rect2, option));