问题描述
假设我有以下模型类:
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反序列化到该类中:
and I want to deserialize following JSON into that class:
{
"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
.
Result object can be different each time (one of 6 models) and its type depends on JSON property type
.
我已经创建了自定义ActionResultConverter
(在Action
类的Result
属性上方的JsonConverter
批注),该自定义ActionResultConverter
应该可以创建特定的 result
对象基于JSON的type
属性中的字符串.
I have created custom ActionResultConverter
(JsonConverter
annotation above Result
property of Action
class) that should be able to create specific result
object based on string in type
property of JSON.
我的问题是我不知道如何从转换器访问该属性,因为只有整个JSON的result
部分才传递给JsonReader
.
My problem is that I don't know how to access that property from converter because only the result
part of whole JSON is passed to JsonReader
.
任何想法或帮助将不胜感激.
Any ideas or help will be appreciated.
谢谢!
推荐答案
Json.NET不提供在反序列化子对象时访问JSON层次结构中父对象的属性值的方法.根据标准,因此无法保证所需的父级属性在JSON流中的子级之前出现.
Json.NET does not provide a method to access the value of a property of a parent object in the JSON hierarchy while deserializing a child object. Likely this is because a JSON object is defined to be an unordered set of name/value pairs, according to the standard, so there can be no guarantee the desired parent property occurs before the child in the JSON stream.
因此,您需要在Action
本身的转换器中处理它,而不是在ActionResult
的转换器中处理Type
属性:
Thus, rather than handling the Type
property in a converter for ActionResult
, you'll need to do it in a converter for Action
itself:
[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) },
};
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];
}
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()
.这样会自动填充除Result
之外的Action
的所有属性,从而避免了手动对每个属性进行反序列化的情况.
Notice the use of JsonSerializer.Populate()
inside ReadJson()
. This automatically fills in all properties of Action
other than Result
, avoiding the need for manual deserialization of each.
这篇关于如何基于另一个JSON属性有条件地反序列化JSON对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!