我有这个对象
public class ConversationAPI
{
[JsonProperty(PropertyName = "lU")]
public DateTime LastUpdated { get; set; }
[JsonProperty(PropertyName = "m", TypeNameHandling = TypeNameHandling.All)]
public List<Message> Messages { get; set; }
}
我从 API 作为 json 发送,并在我的客户端应用程序中反序列化。
The List<Message> Messages property contains either
[Serializable]
public class Message
{
[JsonProperty(PropertyName = "t")]
public string Text { get; set; }
[JsonProperty(PropertyName = "ty")]
public MessageType Type { get; set; }
}
或
[Serializable]
public class DerivedMessage : Message
{
[JsonProperty(PropertyName = "sos")]
public string SomeOtherStuff{ get; set; }
}
我似乎无法反序列化派生类型的数组。我试过这个
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full
};
var conversation = JsonConvert.DeserializeObject<ConversationResponse>(response.Content, settings);
我希望列表消息同时具有消息和派生消息对象。
有什么想法吗?谢谢
找到了解决方案。我使用了自定义转换器
public class MessageConverter : JsonCreationConverter<ConversationAPI.Message>
{
private const string SomeOtherStuffField = "sos";
protected override ConversationAPI.Message Create(Type objectType, JObject jObject)
{
if (FieldExists(SomeOtherStuffField , jObject))
{
return new ConversationAPI.DerivedMessage ();
}
return new ConversationAPI.Message();
}
private bool FieldExists(string fieldName, JObject jObject)
{
return jObject[fieldName] != null;
}
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">contents of JSON object that will be deserialized</param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
你会这样使用它:
var jsonText = "{a string of json to convert}"
JsonConverter[] conv = new JsonConverter[] { new MessageConverter() };
var jsonResponse = JsonConvert.DeserializeObject<ConversationAPI>(jsonText, conv);
我花时间测试@Mihai发布的代码。我喜欢这个解决方案,因为它不会更改 json 文件的内容;序列化与往常一样(不添加$type
或其他属性)。反序列化通过检查 JSON 中是否存在派生字段来确定对象是基对象还是派生对象。这不是防弹的,但在大多数情况下效果很好。
我必须修复一些语法才能使其运行并了解它的工作原理。以下是带有工作用法示例的修改代码:
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace DerivedDeSerJson
{
[Serializable]
public class Message
{
public string Text { get; set; }
}
[Serializable]
public class DerivedMessage : Message
{
public string SomeOtherStuff { get; set; }
}
public class ConversationAPI
{
public DateTime LastUpdated { get; set; }
public List<Message> Messages { get; set; }
}
public class MessageConverter : JsonCreationConverter<Message>
{
private const string SomeOtherStuffField = "SomeOtherStuff";
protected override Message Create(Type objectType, JObject jObject)
{
if (FieldExists(SomeOtherStuffField, jObject))
{
return new DerivedMessage();
}
return new Message();
}
private bool FieldExists(string fieldName, JObject jObject)
{
return jObject[fieldName] != null;
}
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">contents of JSON object that will be deserialized</param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
ConversationAPI conversation = new ConversationAPI()
{
LastUpdated = DateTime.Now,
Messages = new List<Message>()
{
new Message() {Text = "Msg1"},
new DerivedMessage() {Text = "Msg2", SomeOtherStuff = "stuff"},
}
};
string jsonText;
JsonSerializer serializer = new JsonSerializer() { Formatting = Formatting.Indented };
using (TextWriter text = new StringWriter())
using (JsonWriter writer = new JsonTextWriter(text))
{
serializer.Serialize(writer, conversation);
jsonText = text.ToString();
}
Console.WriteLine(jsonText);
//Output:
//{
// "LastUpdated": "2020-06-08T17:05:33.7114095+03:00",
// "Messages":
// [
// { "Text": "Msg1" },
// { "SomeOtherStuff": "stuff", "Text": "Msg2" }
// ]
//}
JsonConverter[] conv = new JsonConverter[] { new MessageConverter() };
ConversationAPI jsonResponse = JsonConvert.DeserializeObject<ConversationAPI>(jsonText, conv);
foreach (var msg in jsonResponse.Messages)
{
Console.WriteLine(msg.Text);
Console.WriteLine(msg.ToString()); // Print type name
}
//Output:
// Msg1
// DerivedDeSerJson.Message
// Msg2
// DerivedDeSerJson.DerivedMessage
}
}
}