在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;}//需校验字典中key是否存在,不然会报错bool keyExist = KvDic.ContainsKey(property.Name);//字段为List<int>类型时,将int转为对应的对应的中文if (property.PropertyType == typeof(List<int>) && keyExist){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 && keyExist){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;}//需校验字典中key是否存在,不然会报错bool keyExist = KvDic.ContainsKey(property.Name);//字段为List<int>类型时,将int转为对应的对应的中文if (property.PropertyType == typeof(List<int>) && keyExist){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 && keyExist){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) },});
}
输出结果: