Json是现代化应用系统最常用的数据交换格式,NewLife.Core 从2010年开始就独立实现了Json序列化和反序列化。准确的说,NewLife.Core 内置实现了三套Json序列化,用于不同场景。
Nuget包:NewLife.Core
源码:https://github.com/NewLifeX/X/tree/master/NewLife.Core/Serialization/Json
视频:https://www.bilibili.com/video/BV1Xd4y1i7ga
最佳实践
任意对象转Json字符串,Nuget引用包 NewLife.Core
using namespace NewLife.Serialization; var obj = new { Name = "NewLife", Description = "新生命团队" }; var str = obj.ToJson(true);
快速扩展
在NewLife系列组件中,随处可见ToJson/ToJsonEntity,正是由该模块的 NewLife.Serialization.JsonHelper 实现!
JsonHelper 实现为扩展方法,命名空间是 NewLife.Serialization,主要方法如下:
// 写入对象,得到Json字符串 String ToJson(this Object value, Boolean indented = false); String ToJson(this Object value, Boolean indented, Boolean nullValue, Boolean camelCase); // 从Json字符串中读取对象 Object ToJsonEntity(this String json, Type type); T ToJsonEntity<T>(this String json); // 格式化Json文本 String Format(String json); // Json类型对象转换实体类 T Convert<T>(Object obj);
ToJson可把常见对象序列化为Json字符串。indented用于生成带缩进的可读性很好的Json文本,一般只用于调试。
ToJsonEntity则是把Json字符串反序列化到目标类型,得到目标对象。
这些扩展方法基于IJsonHost接口实现,用户可以自定义IJsonHost并修改JsonHelper.Default即可改变内部序列化行为。
Json分析器
在WebApi接口对接时,简单的接口可能不想写Model类,而又需要拿到Json结果中的值。或者接口返回具有多种模式,例如正常返回和异常返回不同。这些场景,都可以利用Json分析器JsonParser,把Json字符串解析为IDictionary<String,Object> 和 IList<Object> 构成的字典树。理论上,任意Json数据都可以用 IDictionary<String,Object> 和 IList<Object> 嵌套来表示。
例如星尘的客户端接口:
var rs = client.Get<IDictionary<String, Object>>("Config/GetAll", new { appId = AppId, secret = Secret, scope = Scope, version = _version, }); // 增强版返回 if (rs.TryGetValue("configs", out var obj) && obj is IDictionary<String, Object> configs) { var ver = rs["version"].ToInt(-1); if (ver > 0) _version = ver; return configs; }
传参使用匿名对象,内部借助ToJson序列化为字符串,返回时直接要IDictionary<String, Object>,然后取下一级对象,内部正是由JsonParser提供支持。
该接口的返回数据是这样的:
这是很常见的JsonApi返回格式,code不是0或200时,代表异常响应,data中可能就不是预期的格式了。
JsonParser 的用法非常简单,直接传入json字符串new实例化,然后Decode。或者静态Decode。
public JsonParser(String json); public Object Decode(); /// <summary>解码</summary> /// <param name="json"></param> /// <returns></returns> public static IDictionary<String, Object> Decode(String json) { var parser = new JsonParser(json); return parser.ParseValue() as IDictionary<String, Object>; }
基于轻量级理念设计,JsonParser性能非常给力。
Json读写器
快速扩展的ToJson由 JsonWriter 实现,主要成员如下:
/// <summary>使用UTC时间。默认false</summary> public Boolean UseUTCDateTime { get; set; } /// <summary>使用小写名称</summary> public Boolean LowerCase { get; set; } /// <summary>使用驼峰命名</summary> public Boolean CamelCase { get; set; } /// <summary>忽略空值。默认false</summary> public Boolean IgnoreNullValues { get; set; } /// <summary>忽略只读属性。默认false</summary> public Boolean IgnoreReadOnlyProperties { get; set; } /// <summary>忽略注释。默认true</summary> public Boolean IgnoreComment { get; set; } = true; /// <summary>忽略循环引用。遇到循环引用时写{},默认true</summary> public Boolean IgnoreCircle { get; set; } = true; /// <summary>枚举使用字符串。默认false使用数字</summary> public Boolean EnumString { get; set; } /// <summary>缩进。默认false</summary> public Boolean Indented { get; set; } /// <summary>智能缩进,内层不换行。默认false</summary> public Boolean SmartIndented { get; set; } /// <summary>最大序列化深度。超过时不再序列化,而不是抛出异常,默认5</summary> public Int32 MaxDepth { get; set; } = 5; /// <summary>写入对象</summary> /// <param name="value"></param> public void Write(Object value); /// <summary>获取结果</summary> /// <returns></returns> public String GetString();
实例化JsonWriter后,直接Write对象,然后GetString取结果。足够简单,而又具备常见功能,不会拖泥带水!
快速扩展的ToJsonEntity由 JsonReader 实现,主要成员如下:
/// <summary>是否使用UTC时间</summary> public Boolean UseUTCDateTime { get; set; } /// <summary>服务提供者</summary> public IServiceProvider Provider { get; set; } = ObjectContainer.Provider; /// <summary>读取Json到指定类型</summary> /// <typeparam name="T"></typeparam> /// <param name="json"></param> /// <returns></returns> public T Read<T>(String json); /// <summary>读取Json到指定类型</summary> /// <param name="json"></param> /// <param name="type"></param> /// <returns></returns> public Object Read(String json, Type type);
实例化JsonReader后,直接Read读取结果。属性中的Provider很强大,支持Read的模型类带有接口成员或抽象成员,JsonReader内部将借助IServiceProvider得到该接口类或抽象类的真实实现。
例如性能跟踪器中有这么一个接口:
/// <summary>跟踪片段构建器</summary> public interface ISpanBuilder { #region 属性 /// <summary>跟踪器</summary> ITracer Tracer { get; } /// <summary>操作名</summary> String Name { get; set; } /// <summary>开始时间。Unix毫秒</summary> Int64 StartTime { get; set; } /// <summary>结束时间。Unix毫秒</summary> Int64 EndTime { get; set; } /// <summary>采样总数</summary> Int32 Total { get; } /// <summary>错误次数</summary> Int32 Errors { get; } /// <summary>总耗时。所有请求耗时累加,单位ms</summary> Int64 Cost { get; } /// <summary>最大耗时。单位ms</summary> Int32 MaxCost { get; } /// <summary>最小耗时。单位ms</summary> Int32 MinCost { get; } /// <summary>正常采样</summary> IList<ISpan> Samples { get; } /// <summary>异常采样</summary> IList<ISpan> ErrorSamples { get; } #endregion #region 方法 /// <summary>开始一个Span</summary> /// <returns></returns> ISpan Start(); /// <summary>完成Span</summary> /// <param name="span"></param> void Finish(ISpan span); #endregion }
其中的Samples和ErrorSamples类型是IList<ISpan>,ISpan也是接口。星尘在接收数据时,不想再搞一堆实现该接口的模型类,希望能够直接利用默认的DefaultSpanBuilder。因此,可以有以下写法:
var builder = "xxx".ToJsonEntity<ISpanBuilder>();
标准Json序列化
标准版Json序列化框架 NewLife.Serialization.Json 提供了足够强大的可扩展能力,支持各种定制。然而随着Json用法日趋标准化,在序列化上进行定制的意义越来越小!
Json类主要成员如下:
Boolean Write(Object value, Type type = null); Object Read(Type type); T Read<T>(); Boolean TryRead(Type type, ref Object value);
总结
推荐使用 ToJson/ToJsonEntity/JsonParser ,在.NETCore新项目中,推荐使用 System.Text.Json !