使用JsonConverter的OnDeserialized回调 [英] OnDeserialized callback with JsonConverter

查看:124
本文介绍了使用JsonConverter的OnDeserialized回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用此答案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?

推荐答案

创建了

  • 调用序列化回调.
  • 跳过忽略的属性.
  • 对于通过属性附加到该类型成员的转换器调用JsonConverter.ReadJson().
  • 设置默认值,跳过空值,解析引用等.
    • Calling serialization callbacks.
    • Skipping ignored properties.
    • Invoking JsonConverter.ReadJson() for converters attached via attributes to members of the type.
    • Setting default values, skipping null values, resolving references, etc etc.

    完整的逻辑可以在

    The complete logic can be seen in 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.)

    简化此任务的一种方法是使用Json.NET自己的 JsonObjectContract 类型元数据,由 JsonSerializer.ContractResolver.ResolveContract(objectType) .此信息包含反序列化期间Json.NET使用的序列化回调和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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆