C# 对象序列化之序列化为Json文件(一)

目录

 

1.概念

1.1原理

1.2用途

1.3 JSON序列化

1.4 二进制和XML序列化

2. 序列化为JSON

2.1 简单的序列化

2.2 复杂的序列化

3 忽略属性

3.1 忽略单个属性

3.2 忽略所有只读属性

3.3 忽略所有Null的属性

4 序列化为格式化的JSON

5 自定义属性名称和值

5.1 自定义单个属性名称

5.2 对所有 JSON 属性名称使用 camel 大小写

5.3 使用自定义 JSON 属性命名策略

5.4 序列化字典

5.5 作为字符串的枚举

5.6 不区分大小写的属性匹配

6 处理溢出 JSON


1.概念

序列化是指将对象转换成字节流,从而存储对象或将对象传输到内存、数据库或文件的过程。 它的主要用途是保存对象的状态,以便能够在需要时重新创建对象。 反向过程称为“反序列化”。

1.1原理

图:序列化

将对象序列化为带有数据的流。 该流还可能包含有关对象类型的信息,例如其版本、区域性和程序集名称。 可以将此流中的对象存储在数据库、文件或内存中。

1.2用途

通过序列化,开发人员可以保存对象的状态,并能在需要时重新创建对象,同时还能存储对象和交换数据。 通过序列化,开发人员可以执行如下操作:

  • 使用 Web 服务将对象发送到远程应用程序
  • 将对象从一个域传递到另一个域
  • 将对象通过防火墙传递为 JSON 或 XML 字符串
  • 跨应用程序维护安全或用户特定的信息

1.3 JSON序列化

System.Text.Json 命名空间包含用于 JavaScript 对象表示法 (JSON) 序列化和反序列化的类。 JSON 是一种常用于在 Web 上共享数据的开放标准。

JSON 序列化将对象的公共属性序列化为符合 RFC 8259 JSON 规范的字符串、字节数组或流。 若要控制 JsonSerializer 对类的实例进行序列化或反序列化的方法,请执行以下操作:

1.4 二进制和XML序列化

System.Runtime.Serialization 命名空间包含用于对二进制和 XML 进行序列化和反序列化的类。

二进制序列化使用二进制编码来生成精简的序列化以供使用,如基于存储或套接字的网络流。 在二进制序列化中,所有成员(包括只读成员)都会被序列化,且性能也会有所提升。

 警告:二进制序列化可能会十分危险。 有关详细信息,请参阅BinaryFormatter security guide

XML 序列化将对象的公共字段和属性或方法的参数和返回值序列化成符合特定 XML 架构定义语言 (XSD) 文档要求的 XML 流。 XML 序列化生成已转换成 XML 的强类型类,其中包含公共属性和字段。 System.Xml.Serialization 包含用于对 XML 进行序列化和反序列化的类。 将特性应用于类和类成员,从而控制 XmlSerializer 如何序列化或反序列化类的实例。

 


2. 序列化为JSON

2.1 简单的序列化

假设有这样一个对象:

    public class WeatherForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string Summary { get; set; }
    }

你要将其实例序列化,并写入到Json文件:

 static void Main(string[] args)
        {

            WeatherForecast weatherForecast = new WeatherForecast
            {
                Date = DateTime.Now,
                TemperatureCelsius = 20,
                Summary = "it is a good day!"
            };
            string jsonString = JsonSerializer.Serialize(weatherForecast);
            // 异步序列化
            //using FileStream createStream = File.Create(fileName);
            //await JsonSerializer.SerializeAsync(createStream, weatherForecast);

            File.WriteAllText("weather.json", jsonString);
}

2.2 复杂的序列化

假设有这样一个更复杂的类:

 public class WeatherForecastWithPOCOs
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string Summary { get; set; }
        public string SummaryField;
        public IList<DateTimeOffset> DatesAvailable { get; set; }
        public Dictionary<string, HighLowTemps> TemperatureRanges { get; set; }
        public string[] SummaryWords { get; set; }
    }

    public class HighLowTemps
    {
        public int High { get; set; }
        public int Low { get; set; }
    }

这次我们实行反序列化实验,假设再你的程序目录下已经有一个Json文件(我取名为:weatherPocos.json):

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": 20,
      "Low": -10
    },
    "Hot": {
      "High": 60,
      "Low": 20
    }
  },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

将Json序列化为WeatherForecastWithPOCOs对象:

            string jsonString2 = File.ReadAllText("weatherPocos.json");
            var weatherPoco = JsonSerializer.Deserialize<WeatherForecastWithPOCOs>(jsonString2);
            //异步操作
            //using FileStream openStream = File.OpenRead(fileName);
            //weatherForecast = await JsonSerializer.DeserializeAsync<WeatherForecast>(openStream);
            Console.WriteLine(weatherPoco.Date);

3 忽略属性

将 C# 对象序列化为 JavaScript 对象表示法 (JSON) 时,默认情况下,所有公共属性都会序列化。 如果不想让某些属性出现在生成的 JSON 中,则有几个选项可用。 本文将介绍如何根据各种条件忽略属性:

3.1 忽略单个属性

若要忽略单个属性,请使用 [JsonIgnore] 特性。

下面是要进行序列化的示例类型和 JSON 输出:

public class WeatherForecastWithIgnoreAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    [JsonIgnore]
    public string Summary { get; set; }
}

这样,当我们序列化上面的对象时,json文件如下:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
}

3.2 忽略所有只读属性

如果属性包含公共 getter 而不是公共 setter,则属性为只读。 若要在序列化时忽略所有只读属性,请将 JsonSerializerOptions.IgnoreReadOnlyProperties 设置为 true,如以下示例中所示:

var options = new JsonSerializerOptions
{
    IgnoreReadOnlyProperties = true,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

举个例子:

定义类如下:

public class WeatherForecastWithROProperty
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    public int WindSpeedReadOnly { get; private set; } = 35;
}

序列化的结果:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
}

此选项仅适用于序列化。 在反序列化过程中,默认情况下会忽略只读属性。

3.3 忽略所有Null的属性

若要在序列化时忽略所有 null 值属性,请将 IgnoreNullValues 属性设置为 true,如以下示例中所示:

var options = new JsonSerializerOptions
{
    IgnoreNullValues = true,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

倘若对象如下:

Property
Date8/1/2019 12:00:00 AM -07:00
TemperatureCelsius25
Summarynull

实际结果为:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25
}

4 序列化为格式化的JSON

默认情况下,序列化得到的JSON文件会被压缩,也就是我们上面看到的树形结构会压缩成一行,如下:

{"Date":"2019-08-01T00:00:00-07:00","TemperatureCelsius":25,"Summary":"Hot","DatesAvailable":["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],"TemperatureRanges":{"Cold":{"High":20,"Low":-10},"Hot":{"High":60,"Low":20}},"SummaryWords":["Cool","Windy","Humid"]}

若要输出带格式的JSON,需要将 JsonSerializerOptions.WriteIndented 设置为 true

如果你通过相同的选项重复使用 JsonSerializerOptions,则请勿在每次使用时都创建新的 JsonSerializerOptions 实例。对每个调用重复使用同一实例。 有关详细信息,请参阅重用 JsonSerializerOptions 实例

5 自定义属性名称和值

默认情况下,序列化为Json的每个key名和你类中的会保持完全一致,但是你也可以进行修改,以达到自定义。

5.1 自定义单个属性名称

若要设置单个属性的名称,请使用 [JsonPropertyName] 特性。下面是要进行序列化的示例类型和生成的 JSON:

public class WeatherForecastWithPropertyNameAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}

你会看到WindSpeed的key 变为Wind:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "Wind": 35
}

此特性设置的属性名称:

  • 同时适用于两个方向(序列化和反序列化)。
  • 优先于属性命名策略。

5.2 对所有 JSON 属性名称使用 camel 大小写

若要对所有 JSON 属性名称使用 camel 大小写,请将 JsonSerializerOptions.PropertyNamingPolicy 设置为 JsonNamingPolicy.CamelCase,如下面的示例中所示:

var serializeOptions = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
public class WeatherForecastWithPropertyNameAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
{
  "date": "2019-08-01T00:00:00-07:00",
  "temperatureCelsius": 25,
  "summary": "Hot",
  "Wind": 35
}

camel 大小写属性命名策略:

  • 适用于序列化和反序列化。
  • 由 [JsonPropertyName] 特性替代。 这便是示例中的 JSON 属性名称 Wind 不是 camel 大小写的原因。

5.3 使用自定义 JSON 属性命名策略

若要使用自定义 JSON 属性命名策略,请创建派生自 JsonNamingPolicy 的类,并替代 ConvertName 方法,如下面的示例中所示:

using System.Text.Json;

namespace SystemTextJsonSamples
{
    public class UpperCaseNamingPolicy : JsonNamingPolicy
    {
        public override string ConvertName(string name) =>
            name.ToUpper();
    }
}

然后,将 JsonSerializerOptions.PropertyNamingPolicy 属性设置为命名策略类的实例:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = new UpperCaseNamingPolicy(),
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

下面是要进行序列化的示例类和 JSON 输出:

public class WeatherForecastWithPropertyNameAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
{
  "DATE": "2019-08-01T00:00:00-07:00",
  "TEMPERATURECELSIUS": 25,
  "SUMMARY": "Hot",
  "Wind": 35
}

JSON 属性命名策略:

  • 适用于序列化和反序列化。
  • 由 [JsonPropertyName] 特性替代。 这便是示例中的 JSON 属性名称 Wind 不是大写的原因。

5.4 序列化字典

如果要序列化的对象的属性为 Dictionary<string,TValue> 类型,则 string 键可转换为 camel 大小写。 为此,请将 DictionaryKeyPolicy 设置为 JsonNamingPolicy.CamelCase,如下面的示例中所示:

var options = new JsonSerializerOptions
{
    DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

使用名为 TemperatureRanges 且具有键值对 "ColdMinTemp", 20 和 "HotMinTemp", 40 的字典序列化对象会产生类似于以下示例的 JSON 输出:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "TemperatureRanges": {
    "coldMinTemp": 20,
    "hotMinTemp": 40
  }
}

字典键的 camel 大小写命名策略仅适用于序列化。 如果对字典进行反序列化,即使为 DictionaryKeyPolicy指定 JsonNamingPolicy.CamelCase,键也会与 JSON 文件匹配

5.5 作为字符串的枚举

默认情况下,枚举会序列化为数字。 若要将枚举名称序列化为字符串,请使用 JsonStringEnumConverter

例如,假设需要序列化以下具有枚举的类:

public class WeatherForecastWithEnum
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public Summary Summary { get; set; }
}

public enum Summary
{
    Cold, Cool, Warm, Hot
}

如果 Summary 为 Hot,则默认情况下序列化的 JSON 具有数值 3:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": 3
}

下面的示例代码序列化枚举名称(而不是数值),并将名称转换为 camel 大小写:

options = new JsonSerializerOptions
{
    WriteIndented = true,
    Converters =
    {
        new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
    }
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

生成的 JSON 类似于以下示例:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "hot"
}
options = new JsonSerializerOptions
{
    Converters =
    {
        new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
    }
};
weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithEnum>(jsonString, options);

5.6 不区分大小写的属性匹配

默认情况下,反序列化会查找 JSON 与目标对象属性之间区分大小写的属性名称匹配。 若要更改该行为,请将 JsonSerializerOptions.PropertyNameCaseInsensitive 设置为 true

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString, options);

下面是具有 camel 大小写属性名称的示例 JSON。 它可以反序列化为具有帕斯卡拼写法属性名称的以下类型。

{
  "date": "2019-08-01T00:00:00-07:00",
  "temperatureCelsius": 25,
  "summary": "Hot",
}
public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

6 处理溢出 JSON

反序列化时,可能会在 JSON 中收到不是由目标类型的属性表示的数据。 例如,假设目标类型如下:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

要反序列化的 JSON 如下:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "temperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

如果将显示的 JSON 反序列化为显示的类型,则 DatesAvailable 和 SummaryWords 属性无处可去,会丢失。 若要捕获额外数据(如这些属性),请将 [JsonExtensionData] 特性应用于类型 Dictionary<string,object> 或 Dictionary<string,JsonElement> 的属性:

public class WeatherForecastWithExtensionData
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    [JsonExtensionData]
    public Dictionary<string, object> ExtensionData { get; set; }
}

将前面显示的 JSON 反序列化为此示例类型时,额外数据会成为 ExtensionData 属性的键值。

Property说明
Date"8/1/2019 12:00:00 AM -07:00" 
TemperatureCelsius0区分大小写的不匹配(JSON 中的 temperatureCelsius),因此未设置属性。
Summary"Hot" 
ExtensionDatatemperatureCelsius: 25因为大小写不匹配,所以此 JSON 属性是额外属性,会成为字典中的键值对。
DatesAvailable[ "8/1/2019 12:00:00 AM -07:00", "8/2/2019 12:00:00 AM -07:00" ]JSON 中的额外属性会成为键值对,将数组作为值对象。
SummaryWords[ "Cool", "Windy", "Humid" ]JSON 中的额外属性会成为键值对,将数组作为值对象。

序列化目标对象时,扩展数据键值对会成为 JSON 属性,就如同它们处于传入 JSON 中一样:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 0,
  "Summary": "Hot",
  "temperatureCelsius": 25,
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

请注意,ExtensionData 属性名称不会出现在 JSON 中。 此行为使 JSON 可以进行往返,而不会丢失任何不会以其他方式进行反序列化的额外数据。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#提供了丰富的工具和库来实现Json数据的序列化和反序列化。在这方面,常用的方法有两种:使用Json.NET库和使用System.Text.Json命名空间下的类来实现。 首先,让我们来看看如何使用Json.NET库进行Json数据的序列化和反序列化。引用提到了这个库。Json.NET是一个功能强大且广泛使用的Json处理库,可以在NuGet包管理器中获取到。下面是一个简单的示例代码: ```csharp // 引入Newtonsoft.Json命名空间 using Newtonsoft.Json; // 定义一个示例类 public class Person { public string Name { get; set; } public int Age { get; set; } } // 序列化对象Json字符串 Person person = new Person { Name = "Alice", Age = 30 }; string jsonStr = JsonConvert.SerializeObject(person); // 反序列化Json字符串到对象 Person deserializedPerson = JsonConvert.DeserializeObject<Person>(jsonStr); ``` 以上示例演示了如何将一个Person对象序列化Json字符串,以及如何将Json字符串反序列化为Person对象。使用Json.NET库的优点是它提供了丰富的功能和灵活的配置选项。 另一种方法是使用.NET Core 3.0及以上版本提供的System.Text.Json命名空间下的类来实现Json数据的序列化和反序列化。这种方法不需要额外的第三方库,可以直接使用系统提供的类。下面是一个示例代码: ```csharp // 引入System.Text.Json命名空间 using System.Text.Json; // 定义一个示例类 public class Person { public string Name { get; set; } public int Age { get; set; } } // 序列化对象Json字符串 Person person = new Person { Name = "Alice", Age = 30 }; string jsonStr = JsonSerializer.Serialize(person); // 反序列化Json字符串到对象 Person deserializedPerson = JsonSerializer.Deserialize<Person>(jsonStr); ``` 以上示例演示了如何使用System.Text.Json命名空间下的类将一个Person对象序列化Json字符串,以及如何将Json字符串反序列化为Person对象。使用System.Text.Json的优点是它是一个轻量级的库,对于简单的Json操作足够方便。 综上所述,C#提供了多种方法来实现Json文件序列化和反序列化,包括使用Json.NET库和System.Text.Json命名空间下的类。具体使用哪种方法取决于你的需求和项目的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值