使用JsonConverter的OnDeserialized回调 [英] OnDeserialized callback with JsonConverter
问题描述
我正在尝试使用此答案的JsonConverter
到 Brian Rogers 将JSON中的嵌套属性映射到平面对象.
I´m trying to use the JsonConverter
from this answer to Can I specify a path in an attribute to map a property in my class to a child property in my JSON? by Brian Rogers to map nested properties in JSON to a flat object.
转换器运行良好,但我需要触发OnDeserialized
回调以填充其他属性,但未触发.如果我不使用转换器,则会触发回调.
The converter works well, but I need to fire the OnDeserialized
callback to fill other properties and it´s not fired. If I don´t use the converter, the callback is fired.
示例:
string json = @"{
'response': {
'code': '000',
'description': 'Response success',
},
'employee': {
'name': 'Test',
'surname': 'Testing',
'work': 'At office'
}
}";
// employee.cs
public class EmployeeStackoverflow
{
[JsonProperty("response.code")]
public string CodeResponse { get; set; }
[JsonProperty("employee.name")]
public string Name { get; set; }
[JsonProperty("employee.surname")]
public string Surname { get; set; }
[JsonProperty("employee.work")]
public string Workplace { get; set; }
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
Workplace = "At Home!!";
}
}
// employeeConverter.cs
public class EmployeeConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
object targetObj = Activator.CreateInstance(objectType);
foreach (PropertyInfo prop in objectType.GetProperties()
.Where(p => p.CanRead && p.CanWrite))
{
JsonPropertyAttribute att = prop.GetCustomAttributes(true)
.OfType<JsonPropertyAttribute>()
.FirstOrDefault();
string jsonPath = (att != null ? att.PropertyName : prop.Name);
JToken token = jo.SelectToken(jsonPath);
if (token != null && token.Type != JTokenType.Null)
{
object value = token.ToObject(prop.PropertyType, serializer);
prop.SetValue(targetObj, value, null);
}
}
return targetObj;
}
public override bool CanConvert(Type objectType)
{
// CanConvert is not called when [JsonConverter] attribute is used
return false;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
如果我在Employee类中添加[JsonConverter(typeof(EmployeeConverter))]
,我将获得:
If I add [JsonConverter(typeof(EmployeeConverter))]
in the Employee class I obtain:
=== With Converter ===
Code: 000
Name: Test
Surname: Testing
Workplace: At office
如果我从Employee类中删除[JsonConverter(typeof(EmployeeConverter))]
,则会获得:
If I remove[JsonConverter(typeof(EmployeeConverter))]
from the Employee class I obtain:
=== With Converter ===
Code:
Name:
Surname:
Workplace: At Home!!
我的目标是获得:
=== With Converter ===
Code: 000
Name: Test
Surname: Testing
Workplace: At Home!!
转换器是否缺少某些东西?
Is the converter missing something?
推荐答案
创建了
The complete logic can be seen in 简化此任务的一种方法是使用Json.NET自己的 JsonConverter.ReadJson()
.
JsonConverter.ReadJson()
for converters attached via attributes to members of the type.JsonSerializerInternalReader.PopulateObject()
, and in theory you might need to make your ReadJson()
method duplicate this method. (But in practice you will likely only implement a small, necessary subset of the logic.)JsonObjectContract
类型元数据,由JsonSerializer.ContractResolver.ResolveContract(objectType)
JsonpropertyAttribute
属性数据的列表.使用此信息的转换器的修改版本如下:
One way to make this task easier is to use Json.NET's own JsonObjectContract
type metadata, as returned by JsonSerializer.ContractResolver.ResolveContract(objectType)
. This information contains the list of serialization callbacks and JsonpropertyAttribute
property data used by Json.NET during deserialization. A modified version of the converter that uses this information would be as follows:
// Modified from this answer https://stackoverflow.com/a/33094930
// To https://stackoverflow.com/questions/33088462/can-i-specify-a-path-in-an-attribute-to-map-a-property-in-my-class-to-a-child-pr/
// By https://stackoverflow.com/users/10263/brian-rogers
// By adding handling of deserialization callbacks and some JsonProperty attributes.
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract ?? throw new JsonException(string.Format("{0} is not a JSON object", objectType));
var jo = JToken.Load(reader);
if (jo.Type == JTokenType.Null)
return null;
else if (jo.Type != JTokenType.Object)
throw new JsonSerializationException(string.Format("Unexpected token {0}", jo.Type));
var targetObj = contract.DefaultCreator();
// Handle deserialization callbacks
foreach (var callback in contract.OnDeserializingCallbacks)
callback(targetObj, serializer.Context);
foreach (var property in contract.Properties)
{
// Check that property isn't ignored, and can be deserialized.
if (property.Ignored || !property.Writable)
continue;
if (property.ShouldDeserialize != null && !property.ShouldDeserialize(targetObj))
continue;
var jsonPath = property.PropertyName;
var token = jo.SelectToken(jsonPath);
// TODO: default values, skipping nulls, PreserveReferencesHandling, ReferenceLoopHandling, ...
if (token != null && token.Type != JTokenType.Null)
{
object value;
// Call the property's converter if present, otherwise deserialize directly.
if (property.Converter != null && property.Converter.CanRead)
{
using (var subReader = token.CreateReader())
{
if (subReader.TokenType == JsonToken.None)
subReader.Read();
value = property.Converter.ReadJson(subReader, property.PropertyType, property.ValueProvider.GetValue(targetObj), serializer);
}
}
// TODO: property.ItemConverter != null
else
{
value = token.ToObject(property.PropertyType, serializer);
}
property.ValueProvider.SetValue(targetObj, value);
}
}
// Handle deserialization callbacks
foreach (var callback in contract.OnDeserializedCallbacks)
callback(targetObj, serializer.Context);
return targetObj;
}
演示小提琴此处.
这篇关于使用JsonConverter的OnDeserialized回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!