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 !