如何告诉 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 类添加了一些额外的属性。您会注意到现在有两个类型为object、UserProp 和UserProp2 的属性,它们都使用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