問題
コントリビュートをしているプロジェクトでシリアライズ/デシリアライズしているStackが毎回反転していることに気が付いた。公式ドキュメントや、NewtonJsonのIssueを発見して問題を理解したのでメモしておく。
static void Main(string[] args)
{
var stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
var json = JsonConvert.SerializeObject(stack);
Console.WriteLine(json);
var restored = JsonConvert.DeserializeObject<Stack<int>>(json);
var count = restored.Count;
for (int i = 0; i < count; i++)
{
Console.WriteLine(restored.Pop());
}
Console.ReadLine();
}
実行結果を見ると反転している。
[3,2,1]
1
2
3
分析
原因を知りたいところだが、Stackの公式ドキュメントを見ると、Stack<T>(IEnumerable<T>)
のコンストラクタは、Stackにリバースして格納するようになっている。StackもIEnumerableを実装しているので
var stack = new Stack<int>();
var stack = new Stack<int>(stack);
とかするとオーダーが反転する。このあたりが問題かもしれない。こちらのチケットを見ると近い将来実装するかもしれない。このチケットを見ると、原因は、Json.NET の実装で次のようにいっている。
Deserialization: if the payload is "[1,2,3]", we return a stack with 3 at the top.
Serialization: if stack's contents are [1, 2, 3] with 3 at the top, we return JSON payload [3,2,1].
今デコンパイラが動かないので見れないけど、こういう振る舞いだというのは理解できた。
問題の解決
カスタムのシリアライザを書いているけど、カスタムなので復元の際にリバースさせることにした。
public static TraceContextBase Restore(string json)
{
if (!string.IsNullOrEmpty(json))
{
var typeName = JObject.Parse(json)["$type"];
Type traceContextType = Type.GetType(typeName.Value<string>());
var restored = JsonConvert.DeserializeObject(
json,
traceContextType,
new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
}) as TraceContextBase;
restored.OrchestrationTraceContexts = new Stack<TraceContextBase>(restored.OrchestrationTraceContexts);
return restored;
}
else
{
return TraceContextFactory.Empty;
}
}