LoginSignup
1
0

More than 3 years have passed since last update.

C# Stack のシリアライズ

Posted at

問題

コントリビュートをしているプロジェクトでシリアライズ/デシリアライズしている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;
            }
        }
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0