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。
我正在尝试创建这样的对象结构 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。