用C#实现一个Json解析器(6)——自定义键名映射

目录

    • 前言
    • KeyAttribute特性
    • 高级语法分析器类
    • 改动

前言

本次我们实现自定义键名映射功能,用户可以指定C#对象的属性和哪个Json键值对构成映射。

注意:示例代码使用了C#8.0的语法特性,如果要在你的机器上运行,请确保安装了.Net Core 3.x开发环境。

KeyAttribute特性

首先创建一个用于提示键名映射的特性:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class KeyAttribute : Attribute
{
    public string KeyName { get; }
    public KeyAttribute(string kname)
    {
        KeyName = kname;
    }
}

KeyName属性表示的是Json中对应的键名,使用方法如下:

[Key("Horse")]
public string Deer { get; set; }

通过这样标记一个属性,我们就实现了传说中的“指鹿为马”。

高级语法分析器类

我们创建一个AdvancedParser类来实现使用特性的高级功能,这个类的大多数方法都和PrimaryParser相同,目前只有ParseObject这个方法不同。为了增强可读性,AdvancedParser中还会引入一些私有方法。

改动

首先是ParseObject方法,一切变动都起源于它:

private object ParseObject(Queue<Token> ctx, Type ttype)
{
	...
    while (true)
    {
        token = ctx.Dequeue();
        CheckSyntax(ref token, TokenType.String);
        key = token.value;
        prop = GetPropertyInfo(ttype, key);
        ...
    }
}

可以看到只有prop = GetPropertyInfo(ttype, key);这句发生了变化。在PrimaryParser中,这个值是直接通过获取对应名字的属性来获取的;而到了AdvancedParser中,这个过程就复杂了起来,因此将其单独提炼到一个方法中。

private PropertyInfo GetPropertyInfo(Type type, string key)
{
    if (!keyAttributeBuffer.ContainsKey(type))
    {
        ScanKeyAttribute(type);
    }
    return keyAttributeBuffer[type].ContainsKey(key) ? keyAttributeBuffer[type][key] : type.GetProperty(key);
}

keyAttributeBuffer是一张缓存了键名映射关系的表:

private ConcurrentDictionary<Type, ConcurrentDictionary<string, PropertyInfo>> keyAttributeBuffer;

这里使用支持并发的字典,因为用户可能在多个线程中改写表的内容。

在GetPropertyInfo方法中,首先判断表中有没有注册对应的类型,如果没有,则进行注册。然后判断要获取的键是否在表中,如果在,说明用户使用了自定义键名映射,返回对应的属性;如果不在,说明用户没有自定义,则使用默认值。

下面来看一下ScanKeyAttribute方法:

private void ScanKeyAttribute(Type type)
{
    keyAttributeBuffer[type] = new ConcurrentDictionary<string, PropertyInfo>();
    KeyAttribute attr;
    foreach (var item in type.GetProperties())
    {
        if ((attr = item.GetCustomAttribute<KeyAttribute>()) != null)
        {
            keyAttributeBuffer[type][attr.KeyName] = item;
        }
    }
}

这个方法对首次进行获取元数据操作的类进行注册,缓存Key特性的信息来提高以后的执行效率。它会遍历对应类型中的每个属性,如果这个属性有Key特性,则注册进表里。

自定义键名映射其实就是扩展了ParseObject过程中获取属性的逻辑,让程序能通过扫描特性来将不同名的属性和键值对关联起来,总体比较简单,容易理解。下次继续对语法分析器进行扩展,让它能够支持用户自定义的转换函数。

你可能感兴趣的:(DiaX的轮子工坊)