在反序列化可能无法解析其类型的特定属性时忽略错误

时间:2014-11-23 11:53:33

标签: c# json serialization json.net

我正在序列化一个具有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放在属性中。

2 个答案:

答案 0 :(得分:1)

  

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

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

  

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

您的假设不正确。您无需提前知道对象的类型即可为其制作转换器。

以下是JsonConverter,可以满足您的需求。它的工作原理是将JSON的未知部分加载到JObject,从$type读取JObject属性,然后尝试将类型名称解析为实际的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,它是null。

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

答案 1 :(得分:0)

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

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

这有效,但又一次 - 它很难看。