XIKEW.COM - 实用教程 - C# 8.0&.NET Core 3.0序列化对象图 - 实用教程,C# 8.0, .NET Core 3.0,Modern Cross-Platform Development, Chapter 09, Working with Files, Streams, and Serialization - 关于《C# 8.0 And .NET Core 3.0 Modern Cross-Platform Development》第九章序列化对象图,还是针对XML和JSON进行了序列化及反序列化的知识点进行了说明,并提出了NetCore3.0发布的高性能JSON方案

C# 8.0&.NET Core 3.0序列化对象图
NETCORE 5/25/2020 9:03:19 PM 阅读:13

关于《C# 8.0 And .NET Core 3.0 Modern Cross-Platform Development》第九章序列化对象图,还是针对XML和JSON进行了序列化及反序列化的知识点进行了说明,并提出了NetCore3.0发布的高性能JSON方案

关键字:C# 8.0, .NET Core 3.0,Modern Cross-Platform Development, Chapter 09, Working with Files, Streams, and Serialization

序列化对象图

序列化是使用指定格式将活动对象转换为字节序列的过程。 反序列化是相反的过程。

可以指定的格式有几十种,但最常见的两种格式是可扩展标记语言(XML)和JavaScript对象表示法(JSON)。

更优方案:JSON更紧凑,最适合Web和移动应用程序。 XML较为冗长,但在更多旧系统中得到更好的支持。 使用JSON可以最小化序列化对象图的大小。 将对象图发送到Web应用程序和移动应用程序时,JSON也是一个不错的选择,因为JSON是JavaScript的本机序列化格式,并且移动应用程序通常会在有限的带宽上进行调用,因此字节数很重要。

.NET Core具有多个类,这些类将在XML和JSON之间进行序列化。 我们将从查看XmlSerializer和JsonSerializer开始。

序列化为XML

让我们从XML(可能是目前世界上使用最广泛的序列化格式)开始。

  1. 创建一个名为WorkingWithSerialization的新控制台应用程序项目,将其添加到Chapter09工作区,然后选择该项目作为OmniSharp的活动项目。 为了显示一个典型示例,我们将定义一个自定义类来存储有关人的信息,然后使用带有嵌套的Person实例列表创建一个对象图。

  2. 添加一个名为Person的类,该类的Salary属性为protected,这意味着该类只能由其自身和派生类访问。 为了填充薪水,该类具有一个带有单个参数的构造函数来设置初始薪水,如以下代码所示:

using System;
using System.Collections.Generic;
namespace Packt.Shared
{
    public class Person
    {
        public Person(decimal initialSalary)
        {
            Salary = initialSalary;
        }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateOfBirth { get; set; }
        public HashSet<Person> Children { get; set; }
        protected decimal Salary { get; set; }
    }
}
  1. 返回Program.cs,导入以下名称空间:
using System; // DateTime
using System.Collections.Generic; // List<T>, HashSet<T>
using System.Xml.Serialization; // XmlSerializer
using System.IO; // FileStream
using Packt.Shared; // Person
using static System.Console;
using static System.Environment;
using static System.IO.Path;
  1. 将以下语句添加到Main方法:
// create an object graph
var people = new List<Person>
{
    new Person(30000M) { FirstName = "Alice",
    LastName = "Smith",
    DateOfBirth = new DateTime(1974, 3, 14) },
    new Person(40000M) { FirstName = "Bob",
    LastName = "Jones",
    DateOfBirth = new DateTime(1969, 11, 23) },
    new Person(20000M) { FirstName = "Charlie",
    LastName = "Cox",
    DateOfBirth = new DateTime(1984, 5, 4),
    Children = new HashSet<Person>
    { new Person(0M) { FirstName = "Sally",
    LastName = "Cox",
    DateOfBirth = new DateTime(2000, 7, 12) } } }
};
// create object that will format a List of Persons as XML
var xs = new XmlSerializer(typeof(List<Person>));
// create a file to write to
string path = Combine(CurrentDirectory, "people.xml");
using (FileStream stream = File.Create(path))
{
    // serialize the object graph to the stream
    xs.Serialize(stream, people);
}
WriteLine("Written {0:N0} bytes of XML to {1}",
arg0: new FileInfo(path).Length,
arg1: path);
WriteLine();
// Display the serialized object graph
WriteLine(File.ReadAllText(path));
  1. 运行控制台应用程序,查看结果,并注意引发了异常,如以下输出所示: Unhandled Exception: System.InvalidOperationException: Packt.Shared.Person cannot be serialized because it does not have a parameterless constructor.
  2. 返回Person.cs文件,添加以下语句以定义无参数的构造函数,如以下代码所示:
public Person() { }

构造函数不需要执行任何操作,但是它必须存在,以便XmlSerializer可以在反序列化过程中调用它以实例化新的Person实例。

  1. 重新运行控制台应用程序并查看结果,请注意对象图已序列化为XML,并且不包含Salary属性,如以下输出所示:
Written 752 bytes of XML to
/Users/markjprice/Code/Chapter09/WorkingWithSerialization/people.xml
<?xml version="1.0"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Person>
        <FirstName>Alice</FirstName>
        <LastName>Smith</LastName>
        <DateOfBirth>1974-03-14T00:00:00</DateOfBirth>
    </Person>
    <Person>
        <FirstName>Bob</FirstName>
        <LastName>Jones</LastName>
        <DateOfBirth>1969-11-23T00:00:00</DateOfBirth>
    </Person>
    <Person>
        <FirstName>Charlie</FirstName>
        <LastName>Cox</LastName>
        <DateOfBirth>1984-05-04T00:00:00</DateOfBirth>
        <Children>
    <Person>
        <FirstName>Sally</FirstName>
        <LastName>Cox</LastName>
        <DateOfBirth>2000-07-12T00:00:00</DateOfBirth>
    </Person>
    </Children>
    </Person>
</ArrayOfPerson>

生成紧凑的XML

我们可以使用属性代替某些字段的元素来使XML更加紧凑。

  1. 在Person.cs文件中,导入System.Xml.Serialization命名空间。
  2. 用[XmlAttribute]属性装饰除Children之外的所有属性,并为每个属性设置一个简短的名称,如以下代码所示:
[XmlAttribute("fname")]
public string FirstName { get; set; }
[XmlAttribute("lname")]
public string LastName { get; set; }
[XmlAttribute("dob")]
public DateTime DateOfBirth { get; set; }
  1. 重新运行该应用程序,并注意文件的大小已从752减少到462字节,节省了三分之一以上的空间,如以下输出所示:
Written 462 bytes of XML to /Users/markjprice/Code/Chapter09/WorkingWithSerialization/people.xml
<?xml version="1.0"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Person fname="Alice" lname="Smith" dob="1974-03-14T00:00:00" />
    <Person fname="Bob" lname="Jones" dob="1969-11-23T00:00:00" />
    <Person fname="Charlie" lname="Cox" dob="1984-05-04T00:00:00">
    <Children>
        <Person fname="Sally" lname="Cox" dob="2000-07-12T00:00:00" />
    </Children>
    </Person>
</ArrayOfPerson>

反序列化XML文件

现在,让我们尝试将XML文件反序列化为内存中的活动对象。 1.在Main方法的末尾添加语句以打开XML文件,然后反序列化它,如以下代码所示:

using (FileStream xmlLoad = File.Open(path, FileMode.Open))
{
    // deserialize and cast the object graph into a List of Person
    var loadedPeople = (List<Person>)xs.Deserialize(xmlLoad);
    foreach (var item in loadedPeople)
    {
        WriteLine("{0} has {1} children.",
        item.LastName, item.Children.Count);
    }
}
  1. 重新运行该应用程序,并注意从XML文件成功加载了人员,如以下输出所示:
Smith has 0 children.
Jones has 0 children.
Cox has 1 children.

还有许多其他属性可用于控制生成的XML。

更优方案:使用XmlSerializer时,请记住仅包括公共字段和属性,并且类型必须具有无参数的构造函数。 您可以使用属性来自定义输出。

JSON序列化

Newtonsoft.Json是使用JSON序列化格式的最受欢迎的.NET库之一,称为Json.NET。 它是成熟而强大的。

让我们看看它的作用

  1. 编辑WorkingWithSerialization.csproj文件,为Newtonsoft.Json的最新版本添加程序包引用,如以下标记中突出显示的内容:
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
    <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp3.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
    </ItemGroup>
</Project>

优良作法:在Microsoft的NuGet feed上搜索NuGet包,以发现最新支持的版本,如下面的链接所示: https://www.nuget.org/packages/Newtonsoft.Json/ 2. 在Main方法的末尾添加语句以创建文本文件,然后将人员作为JSON序列化到文件中,如以下代码所示:

// create a file to write to
string jsonPath = Combine(CurrentDirectory, "people.json");
using (StreamWriter jsonStream = File.CreateText(jsonPath))
{
    // create an object that will format as JSON
    var jss = new Newtonsoft.Json.JsonSerializer();
    // serialize the object graph into a string
    jss.Serialize(jsonStream, people);
}
WriteLine();
WriteLine("Written {0:N0} bytes of JSON to: {1}", arg0: new FileInfo(jsonPath).Length, arg1: jsonPath);

// Display the serialized object graph
WriteLine(File.ReadAllText(jsonPath));
  1. 重新运行该应用程序,请注意,与带有元素的XML相比,JSON需要的字节数少于一半。 它甚至比使用属性的XML文件还要小,如以下输出所示:
Written 366 bytes of JSON to: /Users/markjprice/Code/Chapter09/WorkingWithSerialization/people.json

[{"FirstName":"Alice","LastName":"Smith","DateOfBirth":"1974-03-14T00:00:00","Children":null},{"FirstName":"Bob","LastName":"Jones","DateOfBirth":"1969-11-23T00:00:00","Children":null},{"FirstName":"Charlie","LastName":"Cox","DateOfBirth":"1984-05-04T00:00:00","Children":[{"FirstName":"Sally","LastName":"Cox","DateOfBirth":"2000-07-12T00:00:00","Children":null}]}]

高性能JSON处理

.NET Core 3.0引入了用于处理JSON的新名称空间:System.Text.Json,该属性通过利用Span 之类的API进行了性能优化。

另外,通过读取UTF-16来实现Json.NET。 由于大多数网络协议(包括HTTP)都使用UTF-8,因此使用UTF-8读写JSON文档的性能更高,并且可以避免将UTF-8与Json.NET的Unicode字符串值进行代码转换。 借助新的API,Microsoft根据情况将性能提高了1.3到5倍。

更多信息:您可以通过以下链接阅读有关新的System.Text.Json API的更多信息: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/

Json.NET的原始作者James Newton-King加入了Microsoft,并且一直与他们合作开发新的JSON类型。 正如他在讨论新JSON API的评论中所说,“ Json.NET不会消失”,如以下屏幕截图所示:

img

更多信息:您可以通过以下链接阅读关于新的JSON api解决的问题的更多信息,包括JamesNK的评论: https://github.com/dotnet/corefx/issues/33115

让我们探索新的JSON API。

  1. 导入System.Threading.Tasks命名空间。
  2. 通过将void更改为async Task,修改Main方法以启用等待任务,如以下代码中突出显示的那样:
static async Task Main(string[] args)
  1. 导入新的JSON类以使用别名执行序列化,以避免与我们之前使用的Json.NET名称冲突,如以下代码所示:
using NuJson = System.Text.Json.JsonSerializer;
  1. Add statements to open the JSON file, deserialize it, and output the names and counts of the children of the people, as shown in the following code:
using (FileStream jsonLoad = File.Open(jsonPath, FileMode.Open))
{
    // deserialize object graph into a List of Person
    var loadedPeople = (List<Person>)
    await NuJson.DeserializeAsync(utf8Json: jsonLoad, returnType: typeof(List<Person>));
    foreach (var item in loadedPeople)
    {
        WriteLine("{0} has {1} children.",
        item.LastName, item.Children?.Count);
    }
}
  1. 运行控制台应用程序并查看结果,如以下输出所示:
Smith has children.
Jones has children.
Cox has 1 children.

优良作法:选择Json.NET以提高开发人员的工作效率并选择大型功能集或选择System.Text.Json以提高性能。