Json.NET - CustomCreationConverter中单个属性的默认反序列化行为

11
在以下情况下,当我遇到与我正在反序列化的类型存在的JSON属性时,如何使CrazyItemConverter继续正常工作?
我有一些长这样的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看起来像这样:

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 的转换器。

我曾经使用下面的代码来设置已知属性,但是对于可空类型会抛出异常,并且不尊重我的其他 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);是整体反序列化的方法,但如果您希望按属性进行默认功能,则似乎不起作用。

1个回答

10

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

事实证明,Json.Net 已经内置了此功能(自 5.0 版本 5),因此您不需要一个疯狂的转换器。相反,您只需要使用 [JsonExtensionData] 属性来标记您的字典。(有关更多信息,请参见作者的博客。)

因此,您的 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

3
不引入针对 JSON 的特殊属性,是否可能实现类似的功能? - Stephen Drew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接