我正在使用 NewtonSoft.Json 以 json 格式读取/写入我们的数据。一个(非常简化的)例子是:
{
"$type": "MyNamespace.LandingEvent, MyAssembly",
"TimeOfLanding": "2021-04-11T15:00:00.000Z",
"AirportName": "KLAX",
"AirportRunway": "25L"
}
使用模仿属性的 C# DTO 类。请注意,我们使用 TypeNameHandling
。
我们想将 C# 类更改为更复杂的设置:
class Airport
{
public string Name { get; set; }
public string Runway { get; set; }
}
class LandingEvent
{
public DateTime TimeOfLanding { get; set; }
public Airport Airport { get; set; }
}
这将导致新数据将写入 JSON 中:
{
"$type": "MyNamespace.LandingEvent, MyAssembly",
"TimeOfLanding": "2021-04-11T15:00:00.000Z",
"Airport": {
"Name": "KLAX",
"Runway": "25L"
}
}
但我们仍然需要能够读取旧的 JSON 数据并解析为新的类结构。这就是我目前正在努力解决的问题。
我知道要走的路可能是专门的 JsonConverter。在这方面我有几个问题:
$type
属性并实例化正确的类型? (我重写的 CanConvert()
方法被提供了一个基类的名称(由于实际上下文比这个例子更复杂)。AirportName
存在,我只想进行自定义读取。如果不是这种情况,我该如何回退到默认反序列化?编辑:需要进行一些说明。如果我创建自定义 JsonConverter
,则 CanConvert
将接收类型 EventBase
,但 $type
实际上可以包含"MyNamespace.LandingEvent, MyAssembly"
或 "MyNamespace.TakeoffEvent, MyAssembly"
。因此,我可能需要根据这个值自己实例化返回的对象。不过,我不确定如何。
最佳答案
您可以使用自定义 JsonConverter
在处理多态事件类型和不同的 JSON 格式方面执行双重任务。下面是一个例子。它通过将数据加载到 JObject
中来工作,它可以在其中读取 $type
属性并实例化正确的事件类型。从那里,它将尝试从 JSON 填充事件对象。如果 Airport
反序列化失败,它将尝试读取遗留机场属性并从中填充一个新的 Airport
实例。
class EventConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(BaseEvent).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
string type = (string)obj["$type"];
BaseEvent baseEvent;
if (type.Contains(nameof(TakeoffEvent)))
{
baseEvent = new TakeoffEvent();
}
else
{
baseEvent = new LandingEvent();
}
serializer.Populate(obj.CreateReader(), baseEvent);
if (baseEvent.Airport == null)
{
baseEvent.Airport = new Airport
{
Name = (string)obj["AirportName"],
Runway = (string)obj["AirportRunway"]
};
}
return baseEvent;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
注意:这假设你的类结构实际上是这样的:
class Airport
{
public string Name { get; set; }
public string Runway { get; set; }
}
class BaseEvent
{
public Airport Airport { get; set; }
}
class TakeoffEvent : BaseEvent
{
public DateTime TimeOfTakeoff { get; set; }
}
class LandingEvent : BaseEvent
{
public DateTime TimeOfLanding { get; set; }
}
要使用转换器,请将其添加到JsonSerializerSettings
中的Converters
集合,并将设置传递给DeserializeObject()
:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
Converters = new List<JsonConverter> { new EventConverter() }
};
var baseEvent = JsonConvert.DeserializeObject<BaseEvent>(json, settings);
这是一个工作演示:https://dotnetfiddle.net/jSaq4T
另请参阅:Adding backward compatibility support for an older JSON structure
https://stackoverflow.com/questions/67044973/