前言
在前后端分离的业务开发中, 我们总是需要返回各种各样的数据包格式, 一个良好的 JSON 格式数据包是我们一贯奉行的原则, 下面就利用 JSON.NET 来做一个简单具有跨平台的序列化数据包实现类.
1. 应用 JSON.NET
1.1 首先在项目中引用 NuGet 包
1.2 编写一个 JsonReturn 结果包装类, 继承自 ContentResult , 并重写 ContentResult 方法 ExecuteResult(ActionContext context)
- public partial class JsonReturn : ContentResult
- {
- public int Code { get; protected set; }
- public string Message { get; protected set; }
- public Hashtable Data { get; protected set; } = new Hashtable();
- public bool Success { get { return this.Code == 0; } }
- public JsonReturn(int code, string message) { this.Code = code; this.SetMessage(message); }
- public JsonReturn SetMessage(string value) { this.Message = value; return this; }
- public JsonReturn SetData(params object[] value)
- {
- this.Data.Clear();
- return this.AppendData(value);
- }
- public JsonReturn AppendData(params object[] value)
- {
- if (value?.Length <2)
- return this;
- for (int a = 0; a < value.Length; a += 2)
- {
- if (value[a] == null) continue;
- this.Data[value[a]] = a + 1 < value.Length ? value[a + 1] : null;
- }
- return this;
- }
- private void ToJson(ActionContext context)
- {
- this.ContentType = "text/json;charset=utf-8;";
- this.Content = JsonConvert.SerializeObject(this);
- }
- public override Task ExecuteResultAsync(ActionContext context)
- {
- ToJson(context);
- return base.ExecuteResultAsync(context);
- }
- public override void ExecuteResult(ActionContext context)
- {
- ToJson(context);
- base.ExecuteResult(context);
- }
- /// <summary>
- /// 成功 0
- /// </summary>
public static JsonReturn 成功 { get { return new JsonReturn(0, "成功"); } }
- /// <summary>
- /// 失败 500
- /// </summary>
public static JsonReturn 失败 { get { return new JsonReturn(500, "失败"); } }
}
在 JsonReturn 类中, 定义了一个存储业务数据对象的 Hashtable 对象, 在接口中可以往该对象中写入需要序列化的数据, 并重写了 ContentResult 的 ExecuteResultAsync 和 ExecuteResult 方法, 在方法内实现 JsonResult 对象的序列化, 最后提供了两个静态属性方便调用; 在 JsonReutrn 类中, 最重要的是定义了成功和失败的 Code , 默认 0 = 成功, 500 = 失败, 这样就约定了所有客户端都强制使用该协议, 完成了标准的统一.
1.3 在控制器中将此对象返回
- [HttpGet]
- public ActionResult Get()
- {
- UserInfo info = new UserInfo()
- {
- Age = 22,
- Gender = true,
- Name = "Ron.lang",
- RegTime = DateTime.Now
- };
return JsonReturn. 成功. SetData("detail", info);
}
1.4 运行程序, 得到如下内容
- {
- "Code": 0,
- "Message": "成功",
- "Data": {
- "detail": {
- "Name": "Ron.lang",
- "Gender": true,
- "Age": 22,
- "RegTime": "2018-12-02T16:27:17.3289028+08:00"
- }
- }
- }
2. 改造
2.1 上面的结果还可以接受, 只是有一点小瑕疵, 比如 bool 类型和字段名称大小写的问题, 以及时间格式, 都不是太友好, 对于跨平台来说, 会存在一些问题, 下面我们改造一下, 使得输出的字段名称全部消息, bool 类型转换为数字 0/1, 时间转换为 Unix 格式; 首先创建 3 个自定义 JSON 序列化类
2.2 LowercaseContractResolver.cs 转换字段名称为小写, 该类非常简单, 仅有一行核心代码
- public class LowercaseContractResolver : DefaultContractResolver
- {
- protected override string ResolvePropertyName(string propertyName)
- {
- return propertyName.ToLower();
- }
- }
2.3 BooleanConverter.cs 将 bool 类型转换为数字 0/1
- public class BooleanConverter : JsonConverter
- {
- public override bool CanConvert(Type objectType)
- {
- return objectType == typeof(bool) || objectType == typeof(Nullable<bool>);
- }
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.Value == null)
- return null;
- return Convert.ToBoolean(reader.Value);
- }
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- if (value == null)
- writer.WriteNull();
- else
- {
- UInt32 val = Convert.ToUInt32(Convert.ToBoolean(value));
- writer.WriteValue(val);
- }
- }
- }
2.4 DateTimeConverter.cs Unix 时间格式转换类
- public class DateTimeConverter : DateTimeConverterBase
- {
- public static DateTime Greenwich_Mean_Time = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Local);
- public override bool CanConvert(Type objectType)
- {
- return objectType == typeof(DateTime) || objectType == typeof(Nullable<DateTime>);
- }
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.Value == null)
- return null;
- if (CanConvert(objectType))
- {
- if (string.IsNullOrEmpty(reader.Value.ToNullOrString()))
- return reader.Value;
- if (reader.Value is string)
- {
- if (DateTime.TryParse(reader.Value.ToString(), out DateTime dt))
- return dt;
- else
- return reader.Value;
- }
- else
- return new DateTime(Greenwich_Mean_Time.Ticks + Convert.ToInt64(reader.Value) * 10000).ToLocalTime();
- }
- else
- return reader.Value;
- }
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- if (value == null)
- writer.WriteNull();
- else
- {
- long val = 0;
- if (value.GetType() == typeof(DateTime))
- {
- DateTime dt = Convert.ToDateTime(value);
- val = (dt.ToUniversalTime().Ticks - Greenwich_Mean_Time.Ticks) / 10000;
- }
- else
- val = Convert.ToInt64(value);
- writer.WriteValue(val);
- }
- }
- }
2.5 最后一步, 全局注册 JsonSettings 到系统中, 打开 Startup.cs 文件, 在 Startup 方法中写入以下内容
- public Startup(IConfiguration configuration, IHostingEnvironment env)
- {
- JsonConvert.DefaultSettings = () =>
- {
- var st = new JsonSerializerSettings
- {
- Formatting = Formatting.Indented
- };
- st.Converters.Add(new BooleanConverter());
- st.Converters.Add(new DateTimeConverter());
- st.ContractResolver = new LowercaseContractResolver();
- return st;
- };
- }
2.6 运行程序, 接口输出以下内容, 完成
- {
- "code": 0,
- "message": "成功",
- "data": {
- "detail": {
- "name": "Ron.lang",
- "gender": 1,
- "age": 22,
- "regtime": 1543739815980
- }
- }
- }
结语
通过继承 ContentResult 实现自定义的序列化数据包, 这是刚需; 为了实现跨平台的要求, 我们还自定义 JsonSettings 实现各种类型的自定义转换, 在实际项目开发中, 这是非常有用的.
代码下载
https://files.cnblogs.com/files/viter/Ron.JsonTest.zip
来源: https://www.cnblogs.com/viter/p/10056003.html