在.Net Core 3.0中嘗試新的System.Text.Json API

  • 2019 年 10 月 31 日
  • 筆記

.NET Core 3.0提供了一個名為System.Text.Json的全新命名空間,它支援reader/writer,文檔對象模型(DOM)和序列化程式。在此部落格文章中,我將介紹它如何工作以及如何使用。
官方文檔

獲取JSON庫

  • 如果以.NET Core為目標,請安裝.NET Core 3.0及以上版本,該版本提供了新的JSON庫和ASP.NET Core集成。
  • 如果以.NET Standard或.NET Framework為目標。安裝System.Text.Json NuGet軟體包(確保安裝.NET Framework4.6.0或更高版本)。為了與ASP.NET Core集成,必須以.NET Core 3.0為目標。

NET Core 3.0中JSON特性

新的JSON API通過使用Span進行性能優化,並且可以直接處理UTF-8,而無需轉碼為UTF-16 string 實例。這兩個方面對於ASP.NET Core都是至關重要的,在ASP.NET Core中,吞吐量是關鍵要求。使用System.Text.Json,可以將速度提高大約1.3倍至5倍。

使用System.Text.Json

using System.Text.Json;  using System.Text.Json.Serialization;

使用序列化器Serializer

  • 學習.Net Core最好的方式是查看源碼,下面是JsonSerializer Serialize的部分源碼:
namespace System.Text.Json  {      public static partial class JsonSerializer      {          /// <summary>          /// Convert the provided value into a <see cref="System.String"/>.          /// </summary>          /// <returns>A <see cref="System.String"/> representation of the value.</returns>          /// <param name="value">The value to convert.</param>          /// <param name="options">Options to control the conversion behavior.</param>          /// <remarks>Using a <see cref="System.String"/> is not as efficient as using UTF-8          /// encoding since the implementation internally uses UTF-8. See also <see cref="SerializeToUtf8Bytes"/>          /// and <see cref="SerializeAsync"/>.          /// </remarks>          public static string Serialize<TValue>(TValue value, JsonSerializerOptions options = null)          {              return ToStringInternal(value, typeof(TValue), options);          }            /// <summary>          /// Convert the provided value into a <see cref="System.String"/>.          /// </summary>          /// <returns>A <see cref="System.String"/> representation of the value.</returns>          /// <param name="value">The value to convert.</param>          /// <param name="inputType">The type of the <paramref name="value"/> to convert.</param>          /// <param name="options">Options to control the conversion behavior.</param>          /// <remarks>Using a <see cref="System.String"/> is not as efficient as using UTF-8          /// encoding since the implementation internally uses UTF-8. See also <see cref="SerializeToUtf8Bytes"/>          /// and <see cref="SerializeAsync"/>.          /// </remarks>          public static string Serialize(object value, Type inputType, JsonSerializerOptions options = null)          {              VerifyValueAndType(value, inputType);                return ToStringInternal(value, inputType, options);          }            private static string ToStringInternal(object value, Type inputType, JsonSerializerOptions options)          {              return WriteCoreString(value, inputType, options);          }      }  }

可以看到序列化最終調用的是ToStringInternal這個方法。

  • 示例
class Sample  {      public DateTimeOffset Date { get; set; }      public string Summary { get; set; }  }    string Serialize(Sample value)  {      return JsonSerializer.Serialize<Sample>(value);  }  //obj  string SerializeObj(object value)  {      return JsonSerializer.Serialize(value);  }

JsonSerializerOptions用於在序列化時設置配置, 例如處理注釋,null處理,尾隨逗號和命名策略。

 public static string ToJson(this object obj)   {      var options = new JsonSerializerOptions() { IgnoreNullValues = true, WriteIndented = true, AllowTrailingCommas = true };      return JsonSerializer.Serialize(obj, options);   }

使用反序列化器Deserialize

 Sample Deserialize(string json)  {      var options = new JsonSerializerOptions      {          AllowTrailingCommas = true      };        return JsonSerializer.Deserialize<Sample>(json, options);  }

自定義屬性來控制序列化行為

可以使用自定義屬性來控制序列化行為,例如,忽略屬性或者指定屬性的名稱:

class Sample  {      //忽略      [JsonIgnore]      public DateTimeOffset Date { get; set; }      //指定名稱      [JsonPropertyName("test")]      public string Summary { get; set; }  }

使用DOM

有時,不想反序列化JSON但仍然希望對其內容進行結構化訪問,這時可以使用JsonDocument

var options = new JsonDocumentOptions      {          AllowTrailingCommas = true      };  using (JsonDocument document = JsonDocument.Parse(json, options))  {      //todo       foreach (JsonElement element in document.RootElement.EnumerateArray())       {          DateTimeOffset date = element.GetProperty("date").GetDateTimeOffset();           //todo       }  }

使用Utf8JsonWriter

var options = new JsonWriterOptions  {      Indented = true  };    using (var stream = new MemoryStream())  {      using (var writer = new Utf8JsonWriter(stream, options))      {          writer.WriteStartObject();          writer.WriteString("date", DateTimeOffset.UtcNow);          writer.WriteEndObject();      }        string json = Encoding.UTF8.GetString(stream.ToArray());      Console.WriteLine(json);  }

使用Utf8JsonReader

byte[] data = Encoding.UTF8.GetBytes(json);  Utf8JsonReader reader = new Utf8JsonReader(data, isFinalBlock: true, state: default);    while (reader.Read())  {      Console.Write(reader.TokenType);        switch (reader.TokenType)      {          case JsonTokenType.PropertyName:          case JsonTokenType.String:          {              string text = reader.GetString();              Console.Write(" ");              Console.Write(text);              break;          }            case JsonTokenType.Number:          {              int value = reader.GetInt32();              Console.Write(" ");              Console.Write(value);              break;          }      }        Console.WriteLine();  }

System.Text.Json中的DateTime和DateTimeOffset支援

The System.Text.Json library parses and writes DateTime and DateTimeOffset values according to the ISO 8601:-2019 extended profile. Converters provide custom support for serializing and deserializing with JsonSerializer. Custom support can also be implemented when using Utf8JsonReader and Utf8JsonWriter.

具體請參考官方文檔
示例:

 public class DateTimeConverterUsingDateTimeParse : JsonConverter<DateTime>      {          /// <summary>          /// 日期格式          /// </summary>          public string dateTimeFormat { get; }          /// <summary>          /// ctor          /// </summary>          /// <param name="dateTimeFormat"></param>          public DateTimeConverterUsingDateTimeParse(string dateTimeFormat)          {              this.dateTimeFormat = dateTimeFormat;          }          public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)          {              Debug.Assert(typeToConvert == typeof(DateTime));              return DateTime.Parse(reader.GetString());          }            public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)          {              writer.WriteStringValue(value.ToString(dateTimeFormat));          }      }
 public static class JsonHelper      {          /// <summary>Converts to json.</summary>          /// <param name="obj">The object.</param>          /// <param name="dateTimeFormat">The date time format.</param>          /// <returns>System.String.</returns>          public static string ToJson(this object obj, string dateTimeFormat = "yyyy-MM-dd")          {              var options = new JsonSerializerOptions() { IgnoreNullValues = true, WriteIndented = true };              options.Converters.Add(new DateTimeConverterUsingDateTimeParse(dateTimeFormat));              return JsonSerializer.Serialize(obj, options);          }      }

總結

  • 在.NET Core 3.0中,System.Text.Json API提供對JSON的內置支援,包括reader/writer,只讀DOM和序列化器/反序列化器。
  • 主要目標是實現高性能和低記憶體分配。
  • ASP.NET Core 3.0包括對System.Text.Json的支援,默認情況下啟用。