Newtonsoft Json - 丢失从子对象到父对象的对象引用

Newtonsoft Json - Losing object reference from Child to Parent object

我正在尝试创建这样的对象结构 Universe - 包含 Worlds 的集合 Worlds 包含一系列区域。

所有这 3 种类型都从基 class 扩展而来,基 属性 为 'ParentObject'。 Worlds 将通过此 属性 引用 Universe。 区域将通过此 属性.

引用世界

当 运行 测试工具应用程序时。

            Universe universe = Universe.GetInstance();
            var world = universe.CreateWorld();
            var area = world.CreateArea();

            area.UpdateIpfs();

区域对象上的 UpdateIpfs - 然后从区域到世界再到宇宙向上递归 - 序列化每一层然后添加到 Ipfs 网络。 (这就是我存储 json 数据的地方)

为了取回物品 我愿意

            Universe universe = Universe.GetInstance("QmZnaSaDNnmqhUrE8kFHeu9PGGStAr2D4q3Vt88yHwFvzG");
            var world = universe.Worlds[0];
            var area = world.Areas[0];

单步执行代码我可以看到 json 反序列化前的内容是这样的:

{
  "$id": "1",
  "ParentObject": null,
  "Worlds": [
    {
      "$id": "2",
      "ParentObject": {
        "$ref": "1"
      },
      "Time": "2019-10-06T23:13:56.6002056+01:00",
      "Name": null,
      "Description": null,
      "Areas": [
        {
          "$id": "3",
          "EventScripts": {
            "$id": "4"
          },
          "ParentObject": {
            "$ref": "2"
          },
          "Name": null,
          "IsInterior": false,
          "IsUnderground": false,
          "IsArtificial": false,
          "Description": null,
          "X": 0,
          "Y": 0,
          "Z": 0,
          "AreaObjects": [],
          "ObjType": "BloodlinesLib.Engine.World.Area, BloodlinesLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
          "Hash": "QmbM86GZn6w9JadBM143fDYwGisgNPGXke3bFxpXzrfgJh"
        }
      ],
      "ObjType": "BloodlinesLib.Engine.World.World, BloodlinesLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
      "Hash": "QmRnEfndUDjCTifY694YuftPNWSRHQQJn9WwZmSpUBRJmv"
    }
  ],
  "ObjType": "BloodlinesLib.Engine.World.Universe, BloodlinesLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
  "Hash": null
}

我可以看到区域对象的父对象是 $ref: 2 这似乎与世界对象的 Id 匹配。

但是当我反序列化时 - 'Area' 对象上的 ParentObject 等于 Null。 但是 World 对象上的 ParentObject 已正确分配。

任何人都知道为什么我的 ParentObject 没有针对 Area 的 ParentObject 正确反序列化。

我的序列化和反序列化代码在这里:

public void UpdateIpfs()
        {

            JsonSerializerSettings sets = new JsonSerializerSettings
            {
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                ContractResolver = new NonPublicPropertiesResolver()
            };

            var obj = Convert.ChangeType(this, ObjType);

            if(ObjType == typeof(Universe))
            {
                Hash = null;
            }
            var strContent = JsonConvert.SerializeObject(obj, Formatting.Indented, sets);

            var ipfs = new IpfsClient();
            byte[] data = Encoding.UTF8.GetBytes(strContent);
            data = CompressionHelper.Compress(data);
            using (MemoryStream ms = new MemoryStream(data))
            {
                Hash = ipfs.FileSystem.AddAsync(ms).Result.Id.Hash.ToString();
            }
            if(ParentObject != null)
            {
                ParentObject.UpdateIpfs();
            }
            OnUpdate?.Invoke(this);
        }



public static T LoadFromIpfs<T>(string hash)
        {
            JsonSerializerSettings sets = new JsonSerializerSettings
            {
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                ContractResolver = new NonPublicPropertiesResolver()
            };

            var ipfs = new IpfsClient();
            byte[] data;
            using (MemoryStream ms = new MemoryStream())
            {
                ipfs.FileSystem.ReadFileAsync(hash).Result.CopyTo(ms);
                data =ms.ToArray();
            }
            data = CompressionHelper.Decompress(data);

            string content = Encoding.UTF8.GetString(data);
            T obj = JsonConvert.DeserializeObject<T>(content, sets);
            return obj;
        }

Universe.cs




public class IpfsObject
    {
        public IpfsObject ParentObject;
        public Type ObjType { get; set; }

        public string Hash { get; set; }


        public static T LoadFromIpfs<T>(string hash)
        {
            JsonSerializerSettings sets = new JsonSerializerSettings
            {
                PreserveReferencesHandling = PreserveReferencesHandling.Objects
            };

            var ipfs = new IpfsClient();
            byte[] data;
            using (MemoryStream ms = new MemoryStream())
            {
                ipfs.FileSystem.ReadFileAsync(hash).Result.CopyTo(ms);
                data =ms.ToArray();
            }
            data = CompressionHelper.Decompress(data);

            string content = Encoding.UTF8.GetString(data);
            T obj = JsonConvert.DeserializeObject<T>(content, sets);
            return obj;
        }



        public delegate void OnUpdateDelegate(object obj);
        public event OnUpdateDelegate OnUpdate;

        public void UpdateIpfs()
        {

            JsonSerializerSettings sets = new JsonSerializerSettings
            {
                PreserveReferencesHandling = PreserveReferencesHandling.Objects
            };

            var obj = Convert.ChangeType(this, ObjType);

            if(ObjType == typeof(Universe))
            {
                Hash = null;
            }
            var strContent = JsonConvert.SerializeObject(obj, Formatting.Indented, sets);

            var ipfs = new IpfsClient();
            byte[] data = Encoding.UTF8.GetBytes(strContent);
            data = CompressionHelper.Compress(data);
            using (MemoryStream ms = new MemoryStream(data))
            {
                Hash = ipfs.FileSystem.AddAsync(ms).Result.Id.Hash.ToString();
            }
            if(ParentObject != null)
            {
                ParentObject.UpdateIpfs();
            }
            OnUpdate?.Invoke(this);
        }
    }



    public class Universe : IpfsObject
    {
        public Universe()
        {
            Worlds = new List<World>();
            this.ObjType = typeof(Universe);
            OnUpdate += Universe_OnUpdate;
        }

        private void Universe_OnUpdate(object obj)
        {
            IpfsObject ipfsObj = obj as IpfsObject;
            Console.WriteLine("Universe updated: "+ ipfsObj.Hash);
            File.WriteAllText("UniverseHash.txt", ipfsObj.Hash);
        }

        public World CreateWorld()
        {
            World world = new World(this);
            Worlds.Add(world);
            return world;
        }
        public List<World> Worlds { get; set; }






        public static Universe GetInstance(string hash = null)
        {
            if(_universe == null)
            {
                if(hash == null)
                {
                    _universe = new Universe();
                }
                else
                {
                    _universe = IpfsObject.LoadFromIpfs<Universe>(hash);
                    _universe.Hash = hash;
                }
            }
            return _universe;
        }
        private static Universe _universe;


    }

    public class World : Ipfs.IpfsObject
    {
        public World(Universe universe)
        {
            Time = DateTime.Now;
            ParentObject = universe;
            this.ObjType = typeof(World);
            Areas = new List<Area>();
        }

        public Area CreateArea(bool findFreespace = false)
        {
            Area area = new Area(this);
            Areas.Add(area);
            if (findFreespace)
            {
                bool b = false;
                Random rand = new Random(Guid.NewGuid().GetHashCode());
                while (!b)
                {
                    b = area.SetPosition(rand.Next(-500, 500), rand.Next(-500, 500), 0);
                }
            }
            area.UpdateIpfs();
            return area;
        }

        public void SetName(string name)
        {
            Name = name;
            UpdateIpfs();
        }
        public void SetDescription(string desc)
        {
            Description = desc;
            UpdateIpfs();
        }

        public DateTime Time { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public List<Area> Areas { get; set; }


    }



    public class Area : IpfsObject
    {
        public Area(World worldParent)
        {
            this.ObjType = typeof(Area);
            ParentObject = worldParent;
            AreaObjects = new List<AreaObject>();

        }

        public string Name { get; private set; }
        public bool IsInterior { get; private set; }
        public bool IsUnderground { get; private set; }
        public bool IsArtificial { get; private set; }
        public string Description { get; private set; }


        public void SetName(string name)
        {
            Name = name;
            UpdateIpfs();
        }
        public void SetDescription(string desc)
        {
            Description = desc;
            UpdateIpfs();
        }

        public void SetInterior(bool interior)
        {
            IsInterior = interior;
            UpdateIpfs();
        }
        public void SetUnderground(bool underground)
        {
            IsUnderground = underground;
            UpdateIpfs();
        }
        public void SetArtificial(bool artificial)
        {
            IsArtificial = artificial;
            UpdateIpfs();
        }


        public int X { get; set; }
        public int Y { get; set; }
        public int Z { get; set; }

        public bool SetPosition(int x,int y, int z)
        {
            World world = (World)ParentObject;
            var area = world.Areas.FirstOrDefault(e => e.X == x && e.Y == y && e.Z == z);
            if(area != null)
            {
                return false;
            }
            this.X = x;
            this.Y = y;
            this.Z = z;
            UpdateIpfs();
            return true;
        }


    }

我可以确认,当我从 World/Area 的构造函数中取出参数时,反序列化会保留 world 和 area 之间的引用 link。