C# 使用JsonConverter的OnDeserialized回调

C# 使用JsonConverter的OnDeserialized回调,c#,json.net,json-deserialization,C#,Json.net,Json Deserialization,我试图使用JsonConverterfrom-to-by将JSON中的嵌套属性映射到平面对象 转换器工作得很好,但我需要启动OnDeserialized回调来填充其他属性,但它没有启动。如果我不使用转换器,则会触发回调 示例: stringjson=@”{ ‘回应’:{ “代码”:“000”, '说明':'响应成功', }, “雇员”:{ “名称”:“测试”, ‘姓’:‘测试’, “工作”:“在办公室” } }"; //employee.cs 公共类EmployeeStackoverflow

我试图使用
JsonConverter
from-to-by将JSON中的嵌套属性映射到平面对象

转换器工作得很好,但我需要启动
OnDeserialized
回调来填充其他属性,但它没有启动。如果我不使用转换器,则会触发回调

示例:

stringjson=@”{
‘回应’:{
“代码”:“000”,
'说明':'响应成功',
},
“雇员”:{
“名称”:“测试”,
‘姓’:‘测试’,
“工作”:“在办公室”
}
}";
//employee.cs
公共类EmployeeStackoverflow
{
[JsonProperty(“response.code”)]
公共字符串代码响应{get;set;}
[JsonProperty(“employee.name”)]
公共字符串名称{get;set;}
[JsonProperty(“员工姓名”)]
公共字符串姓氏{get;set;}
[JsonProperty(“employee.work”)]
公共字符串工作区{get;set;}
[已序列化]
内部void OnDeserializedMethod(StreamingContext上下文)
{
Workplace=“在家!!”;
}
}
//employeeConverter.cs
公共类EmployeeConverter:JsonConverter
{
公共重写对象ReadJson(JsonReader reader,类型objectType,
对象存在值,JsonSerializer序列化程序)
{
JObject jo=JObject.Load(读卡器);
object targetObj=Activator.CreateInstance(objectType);
foreach(objectType.GetProperties()中的PropertyInfo属性)
.Where(p=>p.CanRead和p.CanWrite))
{
JsonPropertyAttribute att=prop.GetCustomAttributes(true)
第()类
.FirstOrDefault();
字符串jsonPath=(att!=null?att.PropertyName:prop.Name);
JToken-token=jo.SelectToken(jsonPath);
if(token!=null&&token.Type!=JTokenType.null)
{
对象值=token.ToObject(prop.PropertyType,序列化程序);
prop.SetValue(targetObj,value,null);
}
}
返回targetObj;
}
公共覆盖布尔CanConvert(类型objectType)
{
//使用[JsonConverter]属性时不调用CanConvert
返回false;
}
公共覆盖布尔可写
{
获取{return false;}
}
公共重写void WriteJson(JsonWriter writer,对象值,
JsonSerializer(序列化程序)
{
抛出新的NotImplementedException();
}
}
如果我在Employee类中添加
[JsonConverter(typeof(EmployeeConverter))]
,我将获得:

=== With Converter ===
Code: 000
Name: Test
Surname: Testing
Workplace: At office
=== With Converter ===
Code:
Name:
Surname:
Workplace: At Home!!
如果我从Employee类中删除
[JsonConverter(typeof(EmployeeConverter))]
,我将获得:

=== With Converter ===
Code: 000
Name: Test
Surname: Testing
Workplace: At office
=== With Converter ===
Code:
Name:
Surname:
Workplace: At Home!!
我的目标是获得:

=== With Converter ===
Code: 000
Name: Test
Surname: Testing
Workplace: At Home!!
转换器缺少什么吗?

一旦为类型创建了一个,转换器就有责任处理反序列化过程中需要执行的所有操作,包括

  • 呼叫
  • 跳过被忽略的属性
  • 为通过属性连接到类型成员的转换器调用
    JsonConverter.ReadJson()
  • 设置默认值、跳过空值、解析引用等
完整的逻辑可以在中看到,理论上,您可能需要使
ReadJson()
方法与此方法重复。(但在实践中,您可能只实现一小部分逻辑。)

使此任务更容易的一种方法是使用Json.NET自己的类型元数据,如返回的。此信息包含Json.NET在反序列化过程中使用的序列化回调和属性数据列表。使用此信息的转换器的修改版本如下:

// 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;
}

演示小提琴。

“转换器缺少什么东西吗?”-我们如何判断?你还没有发布它的代码。它在链接中,但我会编辑答案。。谢谢我明白你告诉我的一切,我以为转换器只用于属性。我认为获得嵌套属性会更容易,最终对于像我这样的新手来说,转换器非常困难。我已经测试了您的实现,它工作得非常好。就我的案例而言,即使TODO缺失也足够了。非常感谢您不辞辛劳地实施它。