【问题标题】:Unity Json.net bson Self referencing loopunity Json.net bson 自引用循环
【发布时间】:2019-10-31 01:16:52
【问题描述】:

我正在尝试使用 Json.net 在我的游戏中保存内容。使用this 资源,我将游戏保存为 JSON,但现在我想将其保存为 Bson 格式,因为我不希望我的玩家能够轻松地编辑保存文件。

这里的代码有效,正在将我的游戏数据保存到 json。

File.WriteAllText(path, JsonConvert.SerializeObject(objectToSave, Formatting.Indented,
    new JsonSerializerSettings
    {
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    }));

我在这里尝试将我的游戏数据保存为 bson 格式,但我不太清楚如何关闭 bson 格式的 ReferenceLoopHandling。

using (var stream = new MemoryStream())
{
    var serializer = new JsonSerializer();
    var writer = new BsonWriter(stream);
    serializer.ReferenceLoopHandling.Equals(false);
    serializer.Serialize(writer, objectToSave);

    File.WriteAllText(path, serializer.ToString());
}

当我运行此代码时,我收到以下错误。

JsonSerializationException: Self referencing loop detected for property 'graph' with type 'StoryGraph'. Path 'nodes[0]'.

【问题讨论】:

    标签: unity3d json.net bson


    【解决方案1】:

    您可以使用工厂方法JsonSerializer.CreateDefault(JsonSerializerSettings)JsonSerializer.Create(JsonSerializerSettings) 制造具有所需设置的序列化器,然后使用以下扩展方法直接序列化到文件:

    public static partial class BsonExtensions
    {
        // In Json.NET 10.0.1 and later use https://www.nuget.org/packages/Newtonsoft.Json.Bson
        public static void SerializeToFile<T>(T obj, string path, JsonSerializerSettings settings = null)
        {
            using (var stream = new FileStream(path, FileMode.Create))
            using (var writer = new BsonWriter(stream)) // BsonDataWriter in Json.NET 10.0.1 and later
            {
                JsonSerializer.CreateDefault(settings).Serialize(writer, obj);
            }
        }
    
        public static T DeserializeFromFile<T>(string path, JsonSerializerSettings settings = null)
        {
            using (var stream = new FileStream(path, FileMode.Open))
            using (var reader = new BsonReader(stream)) // BsonDataReader in Json.NET 10.0.1 and later
            {
                var serializer = JsonSerializer.CreateDefault(settings);
                //https://www.newtonsoft.com/json/help/html/DeserializeFromBsonCollection.htm
                if (serializer.ContractResolver.ResolveContract(typeof(T)) is JsonArrayContract)
                    reader.ReadRootValueAsArray = true;
                return serializer.Deserialize<T>(reader);
            }
        }
    }
    

    然后序列化如下:

    BsonExtensions.SerializeToFile(objectToSave, path, 
                                   new JsonSerializerSettings 
                                   { 
                                       ReferenceLoopHandling = ReferenceLoopHandling.Ignore 
                                   });
    

    注意事项:

    • 确保在反序列化时使用相同的设置。

    • BSON 支持已移至 Json.NET 10.0.1 中自己的包 Newtonsoft.Json.Bson。在此版本或更高版本中,您应该使用BsonDataWriter(和BsonDataReader),因为BsonWriter 已过时,最终将被删除。

    • serializer.ToString() 不会返回序列化的 BSON;而是使用MemoryStream.ToArray(),即

      File.WriteAllBytes(path, stream.ToArray());
      

      但是,如上面的扩展方法所示,直接流式传输到文件更有效。

    • serializer.ReferenceLoopHandling.Equals(false); 不是在 c# 中设置 ReferenceLoopHandling 属性的正确方法。而是将其设置为一个字段:

       serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
      

      请参阅:Using Properties (C# Programming Guide)

    演示小提琴here.

    【讨论】:

    • 这几乎可以完美运行。我能够序列化到 Bson 但无法反序列化。我收到此错误:
    • JsonSerializationException: 无法将当前 JSON 对象(例如 {"name":"value"})反序列化为类型“System.Collections.Generic.List`1[Item]”,因为该类型需要 JSON数组(例如 [1,2,3])正确反序列化。要修复此错误,要么将 JSON 更改为 JSON 数组(例如 [1,2,3]),要么将反序列化类型更改为普通的 .NET 类型(例如,不是像整数这样的原始类型,而不是像这样的集合类型可以从 JSON 对象反序列化的数组或 List)。 JsonObjectAttribute 也可以添加到类型中,以强制它从 JSON 对象反序列化。
    • @AndrewPullins - 当您尝试将 JSON 对象反序列化为集合时会出现该错误,请参阅Cannot deserialize the current JSON object (e.g. {“name”:“value”}) into type 'System.Collections.Generic.List`1。或者,如果您尝试将集合反序列化为 BSON 的根对象,请参阅Bson array (de)serialization with Json.NET。但我们需要看到minimal reproducible example 才能确定,而您的问题不包括。
    • @AndrewPullins - 如dotnetfiddle.net/KzDbJ1 所示设置ReadRootValueAsArray 能解决您的问题吗?
    • 感谢@dbc,ReadRootValueAsArray 使它工作!这非常有效。非常感谢。
    【解决方案2】:

    也可以直接设置序列化器:

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
        // Fix: Ignore loops
        serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        ...
    

    这解决了我在 Unity C# 上下文中的问题。

    【讨论】:

      猜你喜欢
      • 2012-11-10
      • 2017-03-21
      • 2014-04-13
      • 2017-02-13
      • 2016-09-15
      • 2012-09-01
      • 1970-01-01
      相关资源
      最近更新 更多