假设我有以下模型类:
public class Action
{
public enum Type
{
Open,
Close,
Remove,
Delete,
Reverse,
Alert,
ScaleInOut,
Nothing
}
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("active")]
[JsonConverter(typeof(IntToBoolConverter))]
public bool Active { get; set; }
[JsonProperty("type")]
[JsonConverter(typeof(ActionTypeConverter))]
public Type ActionType { get; set; }
[JsonProperty("result")]
[JsonConverter(typeof(ActionResultConverter))]
public ActionResult Result { get; set; }
}
我想将以下 JSON 反序列化为该类:
{
"name":"test1",
"id":"aa0832f0508bb580ce7f0506132c1c13",
"active":"1",
"type":"open",
"result":{
"property1":"buy",
"property2":"123.123",
"property3":"2016-07-16T23:00:00",
"property4":"768",
"property5":true
}
}
结果对象每次都可以不同(6 个模型之一),其类型取决于 JSON 属性type
。
我已经创建了自定义ActionResultConverter
(JsonConverter
类属性上面的注释Result
Action
属性),它应该能够基于type
JSON 属性中的字符串创建特定的result
对象。
我的问题是我不知道如何从转换器访问该属性,因为只有整个 JSON 的result
部分传递给JsonReader
。
任何想法或帮助将不胜感激。
谢谢!
反序列化子对象时访问 JSON 层次结构中父对象的属性值的方法。 这可能是因为根据标准,JSON 对象被定义为一组无序的名称/值对,因此无法保证所需的父属性出现在 JSON 流中的子属性之前。
因此,与其在转换器中处理 Type
属性以进行ActionResult
,不如在转换器中为Action
本身执行此操作:
[JsonConverter(typeof(ActionConverter))]
public class Action
{
readonly static Dictionary<Type, System.Type> typeToSystemType;
readonly static Dictionary<System.Type, Type> systemTypeToType;
static Action()
{
typeToSystemType = new Dictionary<Type, System.Type>
{
{ Type.Open, typeof(OpenActionResult) },
// Add additional dictionary entries corresponding to each different subtype of ActionResult
};
systemTypeToType = typeToSystemType.ToDictionary(p => p.Value, p => p.Key);
}
public static Type SystemTypeToType(System.Type systemType)
{
return systemTypeToType[systemType];
}
public static System.Type TypeToSystemType(Type type)
{
return typeToSystemType[type];
}
// Add enum values for Type corresponding to each different subtype of ActionResult
public enum Type
{
Open,
Close,
Remove,
Delete,
Reverse,
Alert,
ScaleInOut,
Nothing
}
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("active")]
[JsonConverter(typeof(IntToBoolConverter))]
public bool Active { get; set; }
[JsonProperty("type")]
[JsonConverter(typeof(ActionTypeConverter))]
public Type ActionType { get; set; }
[JsonProperty("result")]
public ActionResult Result { get; set; }
}
class ActionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var obj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var action = existingValue as Action ?? (Action)contract.DefaultCreator();
// Remove the Result property for manual deserialization
var result = obj.GetValue("Result", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent();
// Populate the remaining properties.
using (var subReader = obj.CreateReader())
{
serializer.Populate(subReader, action);
}
// Process the Result property
if (result != null)
action.Result = (ActionResult)result.ToObject(Action.TypeToSystemType(action.ActionType));
return action;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class JsonExtensions
{
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}
请注意在 ReadJson()
中使用JsonSerializer.Populate()
。 这会自动填充Action
除Result
之外的所有属性,避免了手动反序列化每个属性的需要。
演示小提琴在这里:https://dotnetfiddle.net/2I2oVP
灵感来自 http://json.codeplex.com/discussions/56031:
public sealed class ActionModelConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(ActionModel).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
ActionModel actionModel = new ActionModel();
// TODO: Manually populate properties
actionModel.Id = (string)jObject["id"].ToObject<string>();
var type = (ActionModel.Type)jObject["type"].ToObject<ActionModel.Type>();
switch (type)
{
case ActionModel.Type.Open:
var actionResult = jObject["result"].ToObject<ActionOpenResult>(jsonSerializer);
default:
throw new JsonSerializationException($"Unsupported action type: '{type}'");
}
actionModel.Result = actionResult;
return actionModel;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
在编辑器中编码,很抱歉错别字:)