Bootstrap

C#在Json序列化时将key和value转为对应的中文

在C#中,实体类可以通过System.Text.Json或Newtonsoft.Json库等方式直接序列化为json字符串,key为字段(属性)名,value为值。

上面的方式虽然实现简单,但是有个缺陷,就是转化后的json给外人展示时不够直观,key是英文的,有的value甚至是一些代号、枚举之类的,总之就是难以阅读和提取到有效的信息,不能应付老板的离谱需求。

下面介绍一种可行性的方案,在json序列化的时候,将key转为字段中文名,value转为对应的中文描述,通过继承重写Newtonsoft.Json库(演示版本为13.0.3)中的JsonConverter类来实现。

        1、写一个NameAttribute特性

FieldName:属性的中文名称,用于替换属性名(也就是原本的key);IsEnum:是否类枚举,用于处理int类型的代号。

/// <summary>
/// 属性中文名称的特性
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class NameAttribute : Attribute
{
    public string FieldName { get; }
    public bool IsEnum { get; }

    public NameAttribute(string fieldName)
    {
        FieldName = fieldName;
    }

    public NameAttribute(string fieldName, bool isEnum)
    {
        FieldName = fieldName;
        IsEnum = isEnum;
    }
}

        2、写一个KeyValue类,用来存原本value中的代号和对应的中文描述

/// <summary>
/// 键值模型
/// </summary>
public class KeyValue
{
    public KeyValue(int key, string value)
    {
        Key = key; 
        Value = value;
    }

    /// <summary>
    /// 键
    /// </summary>
    public int Key { get; set; }
    
    /// <summary>
    /// 值
    /// </summary>
    public string Value { get; set; }
}

        3、写一个泛型类JsonToChineseConverter<T>,继承JsonConverter

写一个有参构造函数,用来传递参数。这里传的是Dictionary类型,字典的key为实体类的属性名称;字典的value为List<KeyValue>类型,用来存类枚举。

/// <summary>
/// 传入value为list类型的中文名称
/// </summary>
private Dictionary<string, List<KeyValue>> KvDic { get; set; } = new Dictionary<string, List<KeyValue>>();
public JsonToChineseConverter(Dictionary<string, List<KeyValue>> kvDic)
{
    KvDic = kvDic;
}

继承JsonConverter的类需要重写一下3个方法:

        a、重写CanConvert方法,用来校验类型

/// <summary>
/// 校验能够转化的类型
/// </summary>
/// <param name="objectType"></param>
/// <returns></returns>
public override bool CanConvert(Type objectType)
{
    return objectType == typeof(T);
}

        b、重写ReadJson方法,用来读取json字符串,反序列化的时候使用,这里不做实现

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    throw new NotImplementedException("Deserialize is not implemented.");
}

        c、重写WriteJson方法,用来读取实体类字段和值,序列化的时候使用

通过反射的方式取实体类的字段、值和Name特性;忽略value为null、空的字符串、DateTime.MinValue、DateTime.MaxValue的字段,可自行调整。

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    JObject jsonObject = new JObject();
    PropertyInfo[] properties = value.GetType().GetProperties();

    foreach (PropertyInfo property in properties)
    {
        //value为null、空的字符串、DateTime.MinValue、DateTime.MaxValue时,则忽略
        object fieldValue = property.GetValue(value);
        if (fieldValue == null || string.IsNullOrWhiteSpace(fieldValue.ToString()) || (property.PropertyType == typeof(DateTime) && (Convert.ToDateTime(fieldValue) == DateTime.MinValue || Convert.ToDateTime(fieldValue) == DateTime.MaxValue)))
        {
            continue;
        }

        //key默认字段名,有Name注解时则取中文名
        string fieldName = property.Name;

        //取Name注解
        NameAttribute nameAttribute = property.GetCustomAttribute<NameAttribute>();
        if (nameAttribute != null)
        {
            fieldName = nameAttribute.FieldName;
        }

        //字段为List<int>类型时,将int转为对应的对应的中文
        if (property.PropertyType == typeof(List<int>))
        {
            var kv = KvDic[property.Name];
            if (kv != null && kv.Count > 0)
            {
                var fv = fieldValue as List<int>;
                fieldValue = kv.Where(d => fv.Contains(d.Key)).Select(d => d.Value).ToList();
            }
        }
        else if (property.PropertyType == typeof(int) && nameAttribute != null && nameAttribute.IsEnum == true)
        {
            var kv = KvDic[property.Name];
            if (kv != null && kv.Count > 0)
            {
                var fv = fieldValue as int?;
                fieldValue = (kv.FirstOrDefault(d => d.Key == fv.Value)?.Value) ?? fieldValue;
            }
        }
        else if (property.PropertyType == typeof(bool))
        {
            if (Convert.ToBoolean(fieldValue) == true)
            {
                fieldValue = "是";
            }
            else
            {
                fieldValue = "否";
            }
        }

        jsonObject[fieldName] = JToken.FromObject(fieldValue, serializer);
    }

    jsonObject.WriteTo(writer);
}

完整的类代码如下:

/// <summary>
/// 用Newtonsoft.Json序列化JSON时的扩展,将key、value转为对应的中文名称
/// </summary>
/// <typeparam name="T"></typeparam>
public class JsonToChineseConverter<T> : JsonConverter
{
    /// <summary>
    /// 传入value为list类型的中文名称
    /// </summary>
    private Dictionary<string, List<KeyValue>> KvDic { get; set; } = new Dictionary<string, List<KeyValue>>();
    public JsonToChineseConverter(Dictionary<string, List<KeyValue>> kvDic)
    {
        KvDic = kvDic;
    }

    /// <summary>
    /// 校验能够转化的类型
    /// </summary>
    /// <param name="objectType"></param>
    /// <returns></returns>
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    /// <summary>
    /// 读取JSON,不实现
    /// </summary>
    /// <param name="reader"></param>
    /// <param name="objectType"></param>
    /// <param name="existingValue"></param>
    /// <param name="serializer"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Deserialize is not implemented.");
    }

    /// <summary>
    /// 写入JSON
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="value"></param>
    /// <param name="serializer"></param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JObject jsonObject = new JObject();
        PropertyInfo[] properties = value.GetType().GetProperties();

        foreach (PropertyInfo property in properties)
        {
            //value为null、空的字符串、DateTime.MinValue、DateTime.MaxValue时,则忽略
            object fieldValue = property.GetValue(value);
            if (fieldValue == null || string.IsNullOrWhiteSpace(fieldValue.ToString()) || (property.PropertyType == typeof(DateTime) && (Convert.ToDateTime(fieldValue) == DateTime.MinValue || Convert.ToDateTime(fieldValue) == DateTime.MaxValue)))
            {
                continue;
            }

            //key默认字段名,有Name注解时则取中文名
            string fieldName = property.Name;

            //取Name注解
            NameAttribute nameAttribute = property.GetCustomAttribute<NameAttribute>();
            if (nameAttribute != null)
            {
                fieldName = nameAttribute.FieldName;
            }

            //字段为List<int>类型时,将int转为对应的对应的中文
            if (property.PropertyType == typeof(List<int>))
            {
                var kv = KvDic[property.Name];
                if (kv != null && kv.Count > 0)
                {
                    var fv = fieldValue as List<int>;
                    fieldValue = kv.Where(d => fv.Contains(d.Key)).Select(d => d.Value).ToList();
                }
            }
            else if (property.PropertyType == typeof(int) && nameAttribute != null && nameAttribute.IsEnum == true)
            {
                var kv = KvDic[property.Name];
                if (kv != null && kv.Count > 0)
                {
                    var fv = fieldValue as int?;
                    fieldValue = (kv.FirstOrDefault(d => d.Key == fv.Value)?.Value) ?? fieldValue;
                }
            }
            else if (property.PropertyType == typeof(bool))
            {
                if (Convert.ToBoolean(fieldValue) == true)
                {
                    fieldValue = "是";
                }
                else
                {
                    fieldValue = "否";
                }
            }

            jsonObject[fieldName] = JToken.FromObject(fieldValue, serializer);
        }

        jsonObject.WriteTo(writer);
    }
}

        4、单元测试

下面用TestModel类进行测试

public class TestModel()
{
    [Name("ID")]
    public int Id { get; set; }

    [Name("姓名")]
    public string Name { get; set; }

    [Name("性别", true)]
    public int Sex { get; set; }

    [Name("年龄")]
    public int Arg { get; set; }

    [Name("老师")]
    public List<int> Teacher { get; set; }

    [Name("是否毕业")]
    public bool IsGraduation { get; set; }

    public int? Field1 { get; set; } = null;
    public string Field2 { get; set; } = "  ";
    public DateTime Field3 { get; set; } = DateTime.MinValue;
    public DateTime Field4 { get; set; } = DateTime.MaxValue;
    public bool Field5 { get; set; } = false;
}

测试方法如下:

[TestMethod]
public void WriteJsonTest1()
{
    TestModel model = new TestModel()
    {
        Id = 1,
        Name = "张楚岚",
        Sex = 1,
        Arg = 22,
        Teacher = new List<int>() { 1, 2 },
        IsGraduation = true
    };

    Dictionary<string, List<KeyValue>> kvDic = new Dictionary<string, List<KeyValue>>();
    kvDic.Add("Sex", new List<KeyValue>() { new KeyValue(0, "未知"), new KeyValue(1, "男"), new KeyValue(2, "女") });
    kvDic.Add("Teacher", new List<KeyValue>() { new KeyValue(1, "张怀义"), new KeyValue(2, "张之维") });

    string joStr = JsonConvert.SerializeObject(model, new JsonSerializerSettings
    {
        Converters = new List<JsonConverter>() { new JsonToChineseConverter<TestModel>(kvDic) },
    });
}

输出结果:

;