Newtonsoft Json - потеря ссылки на объект из дочернего объекта в родительский - PullRequest
0 голосов
/ 07 октября 2019

Я пытаюсь создать структуру объекта, подобную этой Вселенной - содержит коллекцию миров. Содержит коллекцию областей.

Все 3 из этих типов происходят от базового класса, который имеет свойство 'ParentObject». Миры будут ссылаться на Вселенную через это свойство. Области будут ссылаться на мир через это свойство.

При запуске приложения для тестирования.

            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
}

Я вижу, что ParentObject для объекта area имеет значение $ ref: 2, которое, по-видимому, соответствует идентификатору объекта World.

Но когда я десериализовываю - объект ParentObject наОбъект 'Area' равен Null. Однако объект ParentObject on World назначен правильно.

У всех есть идеи, почему мой ParentObject не десериализован должным образом для 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;
        }


    }

1 Ответ

0 голосов
/ 07 октября 2019

Я могу подтвердить, что когда я беру параметры из конструктора для World / Area - то десериализация затем сохраняет эталонную связь между миром и областью.

...