【问题标题】:Json.NET - Default deserialization behavior for a single property in CustomCreationConverterJson.NET - CustomCreationConverter 中单个属性的默认反序列化行为
【发布时间】:2014-02-27 13:31:42
【问题描述】:

在以下场景中,当CrazyItemConverter 遇到我要反序列化到的类型中存在的 JSON 属性时,如何让它照常进行?

我有一些如下所示的 JSON:

{
    "Item":{
        "Name":"Apple",
        "Id":null,
        "Size":5,
        "Quality":2
    }
}

JSON 被反序列化为一个看起来很像这样的类:

[JsonConverter(typeof(CrazyItemConverter))]
public class Item
{
    [JsonConverter(typeof(CrazyStringConverter))]
    public string Name { get; set; }

    public Guid? Id { get; set; }

    [JsonIgnore]
    public Dictionary<string, object> CustomFields
    {
        get
        {
            if (_customFields == null)
                _customFields = new Dictionary<string, object>();
            return _customFields;
        }
    }

    ...
}

CrazyItemConverter 填充已知属性的值并将未知属性放入 CustomFields。其中的ReadJson 如下所示:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var outputObject = Create(objectType);
    var objProps = objectType.GetProperties().Select(p => p.Name).ToArray();

    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.PropertyName)
        {
            string propertyName = reader.Value.ToString();
            if (reader.Read())
            {
                if (objProps.Contains(propertyName))
                {
                    // No idea :(
                    // serializer.Populate(reader, outputObject);
                }
                else
                {
                    outputObject.AddProperty(propertyName, reader.Value);
                }
            }
        }
    }
    return outputObject;
}

在反序列化过程中,当CrazyItemConverter 遇到已知属性时,我希望它能够正常运行。意思是,尊重[JsonConverter(typeof(CrazyStringConverter))]Name

使用下面的代码来设置已知属性,但是,它会在 nullables 上引发异常并且不尊重我的其他 JsonConverters。

PropertyInfo pi = outputObject.GetType().GetProperty(readerValue, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var convertedValue = Convert.ChangeType(reader.Value, pi.PropertyType);
pi.SetValue(outputObject, convertedValue, null);

有什么想法吗?

更新:我了解到 serializer.Populate(reader, outputObject); 是如何反序列化整个事情,但如果您希望逐个属性地使用默认功能,它似乎不起作用。

【问题讨论】:

    标签: c# json json.net deserialization


    【解决方案1】:

    如果我理解正确,您的 CrazyItemConverter 存在,以便您可以将 JSON 中的已知属性反序列化为强类型属性,同时仍将可能在 JSON 中的“额外”字段保留到字典中。

    事实证明 Json.Net 已经内置了这个功能(从 5.0 第 5 版开始),所以你不需要一个疯狂的转换器。相反,您只需要使用[JsonExtensionData] 属性标记您的字典。 (有关更多信息,请参阅the author's blog。)

    所以你的 Item 类看起来像这样:

    public class Item
    {
        [JsonConverter(typeof(CrazyStringConverter))]
        public string Name { get; set; }
    
        public Guid? Id { get; set; }
    
        [JsonExtensionData]
        public Dictionary<string, object> CustomFields
        {
            get
            {
                if (_customFields == null)
                    _customFields = new Dictionary<string, object>();
                return _customFields;
            }
            private set
            {
                _customFields = value;
            }
        }
        private Dictionary<string, object> _customFields;
    }
    

    然后你可以像往常一样反序列化它。演示:

    class Program
    {
        static void Main(string[] args)
        {
            string json = @"
            {
                ""Item"":
                {
                    ""Name"":""Apple"",
                    ""Id"":""4b7e9f9f-7a30-4f79-8e47-8b50ea26ddac"",
                    ""Size"":5,
                    ""Quality"":2
                }
            }";
    
            Item item = JsonConvert.DeserializeObject<Wrapper>(json).Item;
            Console.WriteLine("Name: " + item.Name);
            Console.WriteLine("Id: " + item.Id);
            foreach (KeyValuePair<string, object> kvp in item.CustomFields)
            {
                Console.WriteLine(kvp.Key + ": " + kvp.Value);
            }
        }
    }
    
    public class Wrapper
    {
        public Item Item { get; set; }
    }
    
    class CrazyStringConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(string);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            // Reverse the string just for fun
            return new string(token.ToString().Reverse().ToArray());
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    输出:

    Name: elppA
    Id: 4b7e9f9f-7a30-4f79-8e47-8b50ea26ddac
    Size: 5
    Quality: 2
    

    【讨论】:

    • 是否可以在不向模型类引入 JSON 特定属性的情况下实现类似的功能?
    猜你喜欢
    • 2015-02-16
    • 1970-01-01
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    相关资源
    最近更新 更多