【问题标题】:Ignoring errors when deserializing a specific property whose type may not be able to be resolved反序列化可能无法解析其类型的特定属性时忽略错误
【发布时间】:2015-01-21 05:01:27
【问题描述】:

我正在序列化具有object 属性的类型,如下所示:

class MyData
{
    ... various properties ...
    object UserProp;
}

由于我使用的是TypeNameHandling.Auto,所以反序列化效果很好,只要反序列化器可以访问相同的程序集。如果解串器无权访问包含UserProp 实际值的程序集,我会得到JsonSerializationException

我想稍微改变一下这种行为。我想尝试反序列化 UserProp 属性,如果失败 - 做点什么。也许引发异常,也许将反序列化的值设置为 null。

如何告诉 JSON.NET 在反序列化特定属性时使用自定义代码?我不能使用JsonConverter,因为我不知道UserProp 的实际类型。

示例: 在序列化方面我有这个代码:

private class MyContext { ... }
var data = new MyData { UserProp = new MyContext(); }

反序列化失败,声称它无法创建 MyContext 类(这是正确的,因为 MyContext 是另一个程序集中的私有类)。我希望反序列化不会失败,而是将 null 放在属性中。

【问题讨论】:

  • 为什么要知道UserProp的实际类型才能使用JsonConverter
  • 我没有。我认为这将有助于解决这个问题,但它没有。
  • 是什么让它失败了?我没有看到任何限制,可以解释一下问题吗?
  • @zmbq 为此可以使用 JsonConverter。请参阅下面的答案。

标签: c# json serialization json.net


【解决方案1】:

我找到了一个可行的解决方案,但它很难看。我已将UserProp 属性标记为非序列化,并添加了一个string _jsonUserProp,它在整个对象的序列化之前进行了序列化。

另一方面,我反序列化对象,在_jsonUserProp 中得到一个 JSON 字符串,然后我反序列化它。如果我得到异常,我将其设置为 null。

这行得通,但又一次 - 它很丑。

【讨论】:

    【解决方案2】:

    如何告诉 JSON.NET 在反序列化特定属性时使用自定义代码?

    使用JsonConverter。这正是它们的设计目的。

    我不能使用 JsonConverter,因为我不知道 UserProp 的实际类型。

    你的假设是错误的。您无需提前知道对象的类型即可为其制作转换器。

    下面是一个JsonConverter,它会做你想做的事。它的工作原理是将 JSON 的未知部分加载到 JObject 中,从 JObject 读取 $type 属性,然后尝试将类型名称解析为实际的 Type。如果成功,它将使用ToObject()JObject 转换为该类型的实例。否则,它只会返回 null。如果在此过程中抛出任何异常,转换器会吃掉异常并返回 null。

    代码如下:

    class UnknownObjectConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return true;
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            try
            {
                JObject jo = JObject.Load(reader);
                string typeName = (string)jo["$type"];
                Type type = Type.GetType(typeName);
                if (type != null)
                {
                    return jo.ToObject(type, serializer);
                }
            }
            catch
            {
            }
    
            return null;
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    要使用此转换器,只需将[JsonConverter] 属性添加到您的UserProp 属性,如下所示:

    class MyData
    {
        [JsonConverter(typeof(UnknownObjectConverter))]
        public object UserProp { get; set; }
    }
    

    注意事项:我发现在某些情况下,GetType() 将无法从外部程序集中解析类型,除非类型名称使用“完整”程序集名称格式。因此,您可能希望在序列化时将TypeNameAssemblyFormat 设置为FormatterAssemblyStyle.Full

    下面是我用来测试转换器的演示。出于演示的目的,我向您的 MyData 类添加了一些额外的属性。您会注意到现在有两个类型为objectUserPropUserProp2 的属性,它们都使用UnknownObjectConverter。我创建了一些 JSON,以便 UserProp 将解析为已知类型 System.Tuple<string>UserProp2 指的是不存在的类型。从输出中可以看出,所有属性都被正确反序列化了,当然UserProp2 除外,它是空的。

    class MyData
    {
        public string Foo { get; set; }
    
        [JsonConverter(typeof(UnknownObjectConverter))]
        public object UserProp { get; set; }
    
        [JsonConverter(typeof(UnknownObjectConverter))]
        public object UserProp2 { get; set; }
    
        public string Bar { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            string json = @"
            {
                ""Foo"": ""fizz"",
                ""UserProp"": {
                    ""$type"": ""System.Tuple`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"",
                    ""Item1"": ""pow""
                },
                ""UserProp2"": {
                    ""$type"": ""JsonTest.Something, JsonTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"",
                    ""Baz"": ""whiff""
                },
                ""Bar"": ""bang""
            }";
    
            MyData data = JsonConvert.DeserializeObject<MyData>(json);
            Console.WriteLine(data.Foo);
            Console.WriteLine(data.Bar);
            Console.WriteLine(((Tuple<string>)data.UserProp).Item1);
            Console.WriteLine(data.UserProp2 == null ? "null" : data.UserProp2.GetType().Name);
        }
    }
    

    输出:

    fizz
    bang
    pow
    null
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-07
      • 2013-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-18
      相关资源
      最近更新 更多