zoukankan      html  css  js  c++  java
  • [译]试用新的System.Text.Json API

    译注

    可能有的小伙伴已经知道了,在.NET Core 3.0中微软加入了对JSON的内置支持。
    一直以来.NET开发者们已经习惯使用Json.NET这个强大的库来处理JSON。
    那么.NET为什么要增加JSON的内置支持呢?
    最近,.NET的官方博客再次发表文章说明了这么做的原因、并介绍了相关API的用法。
    现翻译出来分享给大家。
    
    原文地址: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
    
    

    尝试新的System.Text.Json API

    对于.NET Core 3.0,我们 提供了一个名为System.Text.Json的全新命名空间 ,支持读取器/写入器,文档对象模型(DOM)和序列化。在这篇博文中,我告诉你为什么我们建造它,它是如何工作的,以及你如何试用它。

    我们还有一个视频:

    https://sec.ch9.ms/ch9/f427/704ea54a-dac0-4ef8-b1bc-e9fde129f427/onNET_high.mp4

    获取新的JSON库

    • 如果您的目标是.NET Core。安装最新版本的.NET Core 3.0预览版。这为您提供了新的JSON库和ASP.NET Core集成。
    • 如果您的目标是.NET Standard或.NET Framework。安装System.Text.JsonNuGet包(确保包含预览并安装版本4.6.0-preview6.19303.8或更高版本)。为了与ASP.NET Core集成,您必须以.NET Core 3.0为目标。

    .NET Core 3.0中JSON的未来

    JSON已经成为几乎所有现代.NET应用程序的重要组成部分,并且在许多情况下甚至超过了XML的使用范围。但是,.NET还没有(强大的)内置方式来处理JSON。相反,我们一直依赖Json.NET,它继续为.NET生态系统提供良好的服务。

    我们已经决定构建一个新的JSON库:

    • 提供高性能的JSON API。我们需要一组新的JSON API,这些API通过使用Span<T> 可以高度优化性能, 并且可以直接处理UTF-8而无需转码为UTF-16 string 实例。这两个方面对于ASP.NET Core至关重要,因为吞吐量是关键要求。我们考虑过对Json.NET做出贡献,但如果不破坏现有的Json.NET客户或者破坏我们可以实现的性能,这被认为是不可能的。有了这些 System.Text.Json,我们可以获得1.3倍速--5倍的速度,具体取决于场景(更多细节见下文)。我们相信我们仍然可以挤出更多。

    • 从ASP.NET Core中删除Json.NET依赖项。今天,ASP.NET Core依赖于Json.NET。虽然这提供了ASP.NET Core和Json.NET之间的紧密集成,但它也意味着Json.NET的版本由底层平台决定。但是,Json.NET经常更新,应用程序开发人员通常希望 - 甚至必须 - 使用特定版本。因此,我们希望从ASP.NET Core 3.0中删除Json.NET依赖项,以便客户可以选择使用哪个版本,而不必担心它们可能会意外破坏底层平台。

    • 为Json.NET提供ASP.NET Core集成包。Json.NET基本上已成为.NET中JSON处理的瑞士军刀。它提供了许多选项和工具,使客户可以轻松地处理其JSON需求。我们不想在客户今天获得的Json.NET支持上妥协。例如,能够通过AddJsonOptions 扩展方法在ASP.NET Core中配置JSON序列化 。因此,我们希望为ASP.NET Core提供Json.NET集成作为开发人员可以选择安装的NuGet包,这样他们就可以获得今天从Json.NET获得的所有功能。此工作项的另一部分是确保我们拥有正确的扩展点,以便其他方可以为其选择的JSON库提供类似的集成包。

    有关动机及其与Json.NET的关系的更多详细信息,请查看 我们在10月份发布的公告

    直接使用System.Text.Json

    对于所有示例,请确保导入以下两个名称空间:

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

    使用序列化器

    System.Text.Json 序列化器可以异步读写JSON并且对UTF-8文本进行了优化,使其成为理想的REST API和后端应用程序。

    class WeatherForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureC { get; set; }
        public string Summary { get; set; }
    }
    
    string Serialize(WeatherForecast value)
    {
        return JsonSerializer.ToString<WeatherForecast>(value);
    }
    

    默认情况下,我们生成缩小的JSON。如果要生成易读的内容,可以将 JsonSerializerOptions 的实例传递给序列化程序。这也是您配置其他设置的方式,例如处理注释,尾随逗号和命名策略。

    string SerializePrettyPrint(WeatherForecast value)
    {
        var options = new JsonSerializerOptions
        {
            WriteIndented = true
        };
    
        return JsonSerializer.ToString<WeatherForecast>(value, options);
    }
    

    反序列化的工作方式类似:

    // {
    //      "Date": "2013-01-20T00:00:00Z",
    //      "TemperatureC": 42,
    //      "Summary": "Typical summer in Seattle. Not.",
    // }
    WeatherForecast Deserialize(string json)
    {
        var options = new JsonSerializerOptions
        {
            AllowTrailingCommas = true
        };
    
        return JsonSerializer.Parse<WeatherForecast>(json, options);
    }
    

    我们还支持异步序列化和反序列化:

    async Task SerializeAsync(WeatherForecast value, Stream stream)
    {
        await JsonSerializer.WriteAsync<WeatherForecast>(value, stream);
    }
    

    您还可以使用自定义属性来控制序列化行为,例如,忽略属性并在JSON中指定属性的名称:

    class WeatherForecast
    {
        public DateTimeOffset Date { get; set; }
    
        // 总是使用摄氏度
        [JsonPropertyName("temp")]
        public int TemperatureC { get; set; }
    
        public string Summary { get; set; }
    
        // 不序列化这个属性
        [JsonIgnore]
        public bool IsHot => TemperatureC >= 30;
    }
    

    我们目前不支持F#特定行为(例如有区别的联合和记录类型),但我们 计划将来添加它

    使用DOM

    有时您不希望反序列化JSON有效负载,但您仍希望对其内容进行结构化访问。例如,假设我们有一组温度,并希望计算出星期一的平均温度:

    [
        {
            "date": "2013-01-07T00:00:00Z",
            "temp": 23,
        },
        {
            "date": "2013-01-08T00:00:00Z",
            "temp": 28,
        },
        {
            "date": "2013-01-14T00:00:00Z",
            "temp": 8,
        },
    ]
    

    使用 JsonDocument 类可以很容易地访问各个属性和值。

    double ComputeAverageTemperatures(string json)
    {
        var options = new JsonReaderOptions
        {
            AllowTrailingCommas = true
        };
    
        using (JsonDocument document = JsonDocument.Parse(json, options))
        {
            int sumOfAllTemperatures = 0;
            int count = 0;
    
            foreach (JsonElement element in document.RootElement.EnumerateArray())
            {
                DateTimeOffset date = element.GetProperty("date").GetDateTimeOffset();
    
                if (date.DayOfWeek == DayOfWeek.Monday)
                {
                    int temp = element.GetProperty("temp").GetInt32();
                    sumOfAllTemperatures += temp;
                    count++;
                }
            }
    
            var averageTemp = (double)sumOfAllTemperatures / count;
            return averageTemp;
        }
    }
    

    使用写入器(Writer)

    写入器很容易使用:

    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.WriteNumber("temp", 42);
            writer.WriteEndObject();
        }
    
        string json = Encoding.UTF8.GetString(stream.ToArray());
        Console.WriteLine(json);
    }
    

    读取器需要在不同的token类型间切换处理:

    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;
            }
    
            // Other token types elided for brevity
        }
    
        Console.WriteLine();
    }
    

    与ASP.NET Core集成

    在接受或返回对象有效负载时,通过自动序列化提供ASP.NET内核中大多数JSON的使用,这反过来意味着大多数应用程序的代码与ASP.NET Core正在使用的JSON库无关。这使得从一个切换到另一个变得容易。

    您可以在本文后面看到有关如何在MVC和SignalR中启用新JSON库的详细信息。

    与ASP.NET Core MVC集成

    在预览版5中,ASP.NET Core MVC增加了对使用JSON读写的支持 System.Text.Json。从Preview 6开始,默认情况下使用新的JSON库来序列化和反序列化JSON有效负载。

    可以使用 MvcOptions 配置序列化程序的选项 :

    services.AddControllers()
            .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
    

    如果您想切换回之前的默认使用方式Newtonsoft.Json,请执行以下操作:

    1. 安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet包。

    2. ConfigureServices() 中加入 AddNewtonsoftJson()的调用

      public void ConfigureServices(IServiceCollection services)
      {
          ...
          services.AddControllers()
      +            .AddNewtonsoftJson()
          ...
      }
      

    已知的问题

    • System.Text.JsonOpenAPI / Swagger的支持正在进行,并且不太可能作为3.0版本的一部分提供。

    与SignalR集成

    System.Text.Json 现在是SignalR客户端和服务器在ASP.NET Core 3.0 Preview 5中使用的默认集线协议(Hub Protocol)。

    如果您想切换回以前的默认使用 Newtonsoft.Json,那么您可以在客户端和服务器上执行此操作。

    1. 安装 Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson NuGet包。

    2. 在客户端 HubConnectionBuilder 中添加 .AddNewtonsoftJsonProtocol()

      new HubConnectionBuilder()
          .WithUrl("/chatHub")
          .AddNewtonsoftJsonProtocol()
          .Build();
      
    3. 在服务器AddSignalR()调用中添加 .AddNewtonsoftJsonProtocol()

      services.AddSignalR()
              .AddNewtonsoftJsonProtocol();
      

    性能

    由于此功能受性能的强烈推动,我们希望分享新API的一些高级性能特征。

    请记住,这些都是基于预览版本,最终数字很可能会有所不同。我们还在调整会影响性能的默认行为(例如,区分大小写)。请注意,这些都是微基准测试。您的里程肯定会有所不同,因此如果性能对您至关重要,请确保针对最能代表您工作负载的方案进行自己的测量。如果您遇到希望我们进一步优化的方案,请提交错误

    原生 System.Text.Json

    只需进行微基准测试即可 System.Text.Json 与Json.NET 进行比较 ,得出以下结果:

    场景 速度 内存
    反序列化 快2倍 持平或更低
    序列化 快1.5倍 持平或更低
    文件(只读) 快3-5倍 <1 MB无分配
    读取器 快2-3倍 无分配(直到实现值(materialize values))
    写入器 快1.3-1.6倍 无分配

    ASP.NET Core MVC 中的 System.Text.Json

    我们编写了一个ASP.NET Core应用程序,可以动态生成 数据 ,然后从MVC控制器进行序列化和反序列化 。然后我们改变有效载荷大小并测量结果:

    JSON反序列化(输入)

    描述 吞吐量(RPS) CPU (%) 内存 (MB)
    Newtonsoft.Json – 500 B 136,435 95 172
    System.Text.Json – 500 B 167,861 94 169
    Newtonsoft.Json – 2.4 KB 97,137 97 174
    System.Text.Json – 2.4 KB 32,026 96 169
    Newtonsoft.Json – 40 KB 7,712 88 212
    System.Text.Json – 40 KB 16,625 96 193

    JSON序列化(输出)

    描述 吞吐量(RPS) CPU(%) 内存(MB)
    Newtonsoft.Json - 500 B 120,273 94 174
    System.Text.Json - 500 B 145,631 94 173
    Newtonsoft.Json - 8 KB 35,408 98 187
    System.Text.Json - 8 KB 56,424 97 184
    Newtonsoft.Json - 40 KB 8,416 99 202
    System.Text.Json - 40 KB 14,848 98 197

    对于最常见的有效负载大小, System.Text.Json 在输入和输出格式化期间,MVC的吞吐量增加约20%,内存占用量更小。

    摘要

    在.NET Core 3.0中,我们将发布新的 System.Text.Json API,它们提供对JSON的内置支持,包括读取器/写入器,只读DOM和序列化/反序列化。主要目标是性能,我们看到的一般速度比Json.NET高出2倍,但这取决于您的场景和有效负载,因此请确保衡量对您来说重要的因素。

    ASP.NET Core 3.0包含支持 System.Text.Json,默认情况下已启用。

    试试 System.Text.Json 吧,然后反馈给我们!

    {"happy": "coding!"}

  • 相关阅读:
    摆花
    关于我的博客
    博客美化更新日志
    页面美化代码1.x
    本人已转至新博客!
    回归博客园
    退役快乐
    Luogu神贴合辑
    代码高亮预览
    NOIp2018普及组初赛解题报告
  • 原文地址:https://www.cnblogs.com/waku/p/11026630.html
Copyright © 2011-2022 走看看