我正在序列化一个具有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放在属性中。
答案 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
课程中添加了一些额外的属性。您会注意到现在有两个类型为object
,UserProp
和UserProp2
的属性,这两个属性都使用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。
这有效,但又一次 - 它很难看。