C# .NetCore 中使用 System.Text.Json 序列化 JSON 示例

自定义和控制 JSON 输出,实现JsonSerializerOptions更清洁、高效的数据处理。

介绍

        在这篇文章中,我将深入研究如何使用System.Text.Json库在 C# 中进行 JSON 序列化。我将介绍如何序列化和反序列化对象、处理空值等特殊情况、格式化日期以及使用自定义输出JsonSerializerOptions。 

        导入必要的命名空间。这包括System.Text.JsonJSON 处理和System.Text.Json.Serialization处理转换器和引用处理等高级选项。 

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

    定义一个简单的类,其中包含、、、和 等Product属性。此类表示将序列化为 JSON 的对象。IDProductNameCategoryPricePurchasedDate

public class Product
{
    public int ID {get; set;}
    public string ProductName {get; set;}
    public string Category {get; set;}
    public decimal Price {get; set;}
    public DateTime PurchasedDate {get; set;}
}

创建产品列表:

    现在,让我们创建一个Product包含一些示例数据的对象列表。这些数据将演示 JSON 序列化如何处理不同的数据类型,包括字符串、数字和日期。其中一个产品有一个null值Category,这将帮助我们展示如何JsonSerializer处理空值。

List<Product> products = new List<Product>
{
    new Product { ID = 2, ProductName = "Harry Potter", Category = "Books", Price = 24.99m, PurchasedDate =  new DateTime(2024,09,24,10,20,30) },
    new Product { ID = 3, ProductName = "Console", Category = "Electronics", Price = 199.99m, PurchasedDate = new DateTime(2024,09,25,12,22,45) },
    new Product { ID = 4, ProductName = "Pen", Category = null, Price = 10.0m, PurchasedDate = new DateTime(2024,09,25,06,10,15) }, // Category is null
    new Product { ID = 5, ProductName = "TShirt", Category = "Clothing", Price = 49.99m, PurchasedDate = new DateTime(2024,09,27,08,12,20) },
    new Product { ID = 1, ProductName = "Laptop", Category = "Electronics", Price = 299.99m, PurchasedDate = DateTime.Now }
};


默认 JSON 序列化

让我们首先使用默认设置序列化产品列表。这会将对象图转换为 JSON 字符串,自动处理字符串、小数和日期等类型

string jsonString = JsonSerializer.Serialize(products);

jsonString:
[{"ID":2,"PrductName":"Harry Potter","Category":"Books","Price":24.99,"PurchasedDate":"2024-09-24T10:20:30"},{"ID":3,"PrductName":"Console","Category":"Electronics","Price":199.99,"PurchasedDate":"2024-09-25T12:22:45"},{"ID":4,"PrductName":"Pen","Category":null,"Price":10.0,"PurchasedDate":"2024-09-25T06:10:15"},{"ID":5,"PrductName":"TShirt","Category":"Clothing","Price":49.99,"PurchasedDate":"2024-09-27T08:12:20"},{"ID":1,"PrductName":"Laptop","Category":"Electronics","Price":299.99,"PurchasedDate":"2024-09-27T09:55:51.3731903+09:00"}]

使用控制序列化JsonSerializerOptions
美观打印 (WriteIndented)
WriteIndented = true 可以使输出的 JSON 易于阅读。

options = new JsonSerializerOptions { WriteIndented = true };
json = JsonSerializer.Serialize( products, options);

json:
[
  {
    "ID": 2,
    "PrductName": "Harry Potter",
    "Category": "Books",
    "Price": 24.99,
    "PurchasedDate": "2024-09-24T10:20:30"
  },
  {
    "ID": 3,
    "PrductName": "Console",
    "Category": "Electronics",
    "Price": 199.99,
    "PurchasedDate": "2024-09-25T12:22:45"
  },
  {
    "ID": 4,
    "PrductName": "Pen",
    "Category": null,
    "Price": 10.0,
    "PurchasedDate": "2024-09-25T06:10:15"
  },
  {
    "ID": 5,
    "PrductName": "TShirt",
    "Category": "Clothing",
    "Price": 49.99,
    "PurchasedDate": "2024-09-27T08:12:20"
  },
  {
    "ID": 1,
    "PrductName": "Laptop",
    "Category": "Electronics",
    "Price": 299.99,
    "PurchasedDate": "2024-09-27T09:55:51.3731903+09:00"
  }
]

忽略空值

    使用 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull 在序列化期间忽略空属性。

options = new JsonSerializerOptionsnew JsonSerializerOptions
{
    WriteIndented = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
json = JsonSerializer.Serialize(products, options);

json:
[
  {
    "ID": 2,
    "PrductName": "Harry Potter",
    "Category": "Books",
    "Price": 24.99,
    "PurchasedDate": "2024-09-24T10:20:30"
  },
  {
    "ID": 3,
    "PrductName": "Console",
    "Category": "Electronics",
    "Price": 199.99,
    "PurchasedDate": "2024-09-25T12:22:45"
  },
  {
    "ID": 4,
    "PrductName": "Pen", /* In this case Category is null so it won't appear in the JSON output. */
    "Price": 10.0,
    "PurchasedDate": "2024-09-25T06:10:15"
  },
  {
    "ID": 5,
    "PrductName": "TShirt",
    "Category": "Clothing",
    "Price": 49.99,
    "PurchasedDate": "2024-09-27T08:12:20"
  },
  {
    "ID": 1,
    "PrductName": "Laptop",
    "Category": "Electronics",
    "Price": 299.99,
    "PurchasedDate": "2024-09-27T09:55:51.3731903+09:00"
  }
]

属性名称大小写自定义

    更改属性命名策略,例如使用 CamelCase 或保留原始名称。

options = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
json = JsonSerializer.Serialize(products, options);

json:
[
  {
    "id": 2,
    "prductName": "Harry Potter",
    "category": "Books",
    "price": 24.99,
    "purchasedDate": "2024-09-24T10:20:30"
  },
  {
    "id": 3,
    "prductName": "Console",
    "category": "Electronics",
    "price": 199.99,
    "purchasedDate": "2024-09-25T12:22:45"
  },
  {
    "id": 4,
    "prductName": "Pen",
    "category": null,
    "price": 10.0,
    "purchasedDate": "2024-09-25T06:10:15"
  },
  {
    "id": 5,
    "prductName": "TShirt",
    "category": "Clothing",
    "price": 49.99,
    "purchasedDate": "2024-09-27T08:12:20"
  },
  {
    "id": 1,
    "prductName": "Laptop",
    "category": "Electronics",
    "price": 299.99,
    "purchasedDate": "2024-09-27T09:55:51.3731903+09:00"
  }
]

自定义命名策略

    JsonNamingPolicy如果您需要对字典键的转换有更多的控制,您还可以实现自定义。

public class UpperCaseNamingPolicy : JsonNamingPolicy
{
    public override string ConvertName(string name)
    {
        return name.ToUpper(); // Convert keys to uppercase
    }
}

options = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = new UpperCaseNamingPolicy()
};
json = JsonSerializer.Serialize(products, options);

json:
[
  {
    "ID": 2,
    "PRDUCTNAME": "Harry Potter",
    "CATEGORY": "Books",
    "PRICE": 24.99,
    "PURCHASEDDATE": "2024-09-24T10:20:30"
  },
  {
    "ID": 3,
    "PRDUCTNAME": "Console",
    "CATEGORY": "Electronics",
    "PRICE": 199.99,
    "PURCHASEDDATE": "2024-09-25T12:22:45"
  },
  {
    "ID": 4,
    "PRDUCTNAME": "Pen",
    "CATEGORY": null,
    "PRICE": 10.0,
    "PURCHASEDDATE": "2024-09-25T06:10:15"
  },
  {
    "ID": 5,
    "PRDUCTNAME": "TShirt",
    "CATEGORY": "Clothing",
    "PRICE": 49.99,
    "PURCHASEDDATE": "2024-09-27T08:12:20"
  },
  {
    "ID": 1,
    "PRDUCTNAME": "Laptop",
    "CATEGORY": "Electronics",
    "PRICE": 299.99,
    "PURCHASEDDATE": "2024-09-27T09:55:51.3731903+09:00"
  }
]

创建自定义转换器

    自定义转换器类用于控制特定类型的序列化和反序列化方式。您从 继承JsonConverter<T>,其中T是要自定义的类型。

public class CustomDateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.ParseExact(reader.GetString(), "yyyy-MM-dd", null);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString("yyyy-MM-dd"));
    }
}

options = new JsonSerializerOptions
{
    WriteIndented = true,
};
options.Converters.Add(new CustomDateTimeConverter()); // you can add multiple converters

json = JsonSerializer.Serialize(products, options);

json:
[
  {
    "ID": 2,
    "PrductName": "Harry Potter",
    "Category": "Books",
    "Price": 24.99,
    "PurchasedDate": "2024-09-24"
  },
  {
    "ID": 3,
    "PrductName": "Console",
    "Category": "Electronics",
    "Price": 199.99,
    "PurchasedDate": "2024-09-25"
  },
  {
    "ID": 4,
    "PrductName": "Pen",
    "Category": null,
    "Price": 10.0,
    "PurchasedDate": "2024-09-25"
  },
  {
    "ID": 5,
    "PrductName": "TShirt",
    "Category": "Clothing",
    "Price": 49.99,
    "PurchasedDate": "2024-09-27"
  },
  {
    "ID": 1,
    "PrductName": "Laptop",
    "Category": "Electronics",
    "Price": 299.99,
    "PurchasedDate": "2024-09-27"
  }
]

在此示例中,自定义转换器DateTime用于格式化日期yyyy-MM-dd。

处理大小写敏感问题

    处理 JSON 数据时,处理 JSON 序列化和反序列化中的区分大小写是一个重要方面,因为 JSON 数据中的属性名称可能大小写不同。在 C# 中,可以使用JsonSerializerOptions.PropertyNameCaseInsensitive属性控制区分大小写。

示例 1:区分大小写的反序列化(默认行为)

    默认情况下System.Text.Json区分大小写,这意味着 JSON 中的属性名称必须与 C# 类中的属性名称完全匹配。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
// JSON with property names that don't match C# class case exactly
json = "{\"firstname\": \"Ron\", \"lastname\": \"Weasley\"}";

// Deserialize without setting case-insensitivity (default behavior is case-sensitive)
var person = JsonSerializer.Deserialize<Person>(json);

person:

FirstName: <null>
LastName: <null>

    由于 JSON 中的属性名称(“firstname”和“lastname”)均为小写,而 Person 类具有使用 PascalCase 命名的 FirstName 和 LastName 属性,因此反序列化无法将 JSON 属性映射到类字段,从而使它们保留为空。

示例 2:启用不区分大小写的反序列化

    PropertyNameCaseInsensitive = true您可以通过在 中设置来启用反序列化过程中不区分大小写的属性名称匹配JsonSerializerOptions。这使得反序列化过程忽略 JSON 属性名称和 C# 属性名称之间的大小写差异。

// Enable case-insensitive deserialization
options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
};
// Deserialize with case-insensitivity enabled
person = JsonSerializer.Deserialize<Person>(json, options);

person:

FirstName: Ron
LastName: Weasley

    在这种情况下,即使 JSON 使用小写的属性名称,反序列化也会成功,因为区分大小写已被禁用。
    
处理循环引用

示例 1:启用引用处理

    您可以使用该ReferenceHandler.Preserve选项在 JSON 序列化中启用引用处理。这将通过使用JSON 中的特殊$id和$ref属性来处理循环引用。

    考虑两个类Person和Address,每个类都可以引用另一个类,从而创建循环引用。
    
public class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string City { get; set; }
    public Person Resident { get; set; } // Circular reference back to Person
}

var person = new Person { Name = "Ron Weasley" };
var address = new Address { City = "Hogwarts Gryffindor", Resident = person };
person.Address = address;

// Set up JsonSerializerOptions with ReferenceHandler.Preserve
options = new JsonSerializerOptions
{
    ReferenceHandler = ReferenceHandler.Preserve,  // Enable reference handling
    WriteIndented = true  // Format the JSON for readability
};

// Serialize the object graph with cyclic references
json = JsonSerializer.Serialize(person, options);

json:
{
  "$id": "1",
  "Name": "Ron Weasley",
  "Address": {
    "$id": "2",
    "City": "Hogwarts Gryffindor",
    "Resident": {
      "$ref": "1"
    }
  }
}

var deserializedPerson = JsonSerializer.Deserialize<Person>(json, options);

deserializedPerson:

$id和属性$ref用于管理循环引用。在这种情况下

    *$id: "1"代表Person对象。

    $ref: "1"意味着Address对象的Resident属性引用的是同一个Person对象$id: "1",从而解决了循环引用。

这种方法可以防止无限递归并很好地处理循环引用。

示例 2:忽略循环引用

    如果您想在序列化过程中忽略循环引用(即不序列化会导致循环的属性),则可以使用该ReferenceHandler.IgnoreCycles选项。

// Set up JsonSerializerOptions with ReferenceHandler.IgnoreCycles
var options = new JsonSerializerOptions
{
    ReferenceHandler = ReferenceHandler.IgnoreCycles,  // Ignore circular references
    WriteIndented = true  // Format the JSON for readability
};

 // Serialize the object graph ignoring cyclic references
json = JsonSerializer.Serialize(person, options);

json:
{
  "Name": "Ron Weasley",
  "Address": {
    "City": "Hogwarts Gryffindor",
    "Resident": null
  }
}

deserializedPerson = JsonSerializer.Deserialize<Person>(json, options);

deserializedPerson:

示例 3:使用处理循环引用JsonIgnore

    [JsonIgnore]您还可以通过在可能导致循环的属性上使用属性来手动防止循环引用。
    
public class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string City { get; set; }

    [JsonIgnore]  // Ignore the cyclic reference during serialization
    public Person Resident { get; set; }
}


var person = new Person { Name = "Ron Weasley" };
var address = new Address { City = "Hogwarts Gryffindor", Resident = person };
person.Address = address;

// Serialize the object graph with the [JsonIgnore] attribute
json = JsonSerializer.Serialize(person, new JsonSerializerOptions { WriteIndented = true });

json:
{
  "Name": "Ron Weasley",
  "Address": {
    "City": "Hogwarts Gryffindor"
  }
}

该[JsonIgnore]属性可防止Resident属性被序列化,从而避免循环引用。

这是一种手动控制哪些属性应该从序列化过程中排除的简单方法。

        总之,我们探索了如何使用System.Text.JsonC# 序列化和反序列化对象。我们学习了如何使用和处理空值、日期格式和属性命名约定来自定义 JSON 输出JsonSerializerOptions。我们还探索了如何使用处理大小写敏感性和处理循环引用。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。 

<think>我们被要求不使用Newtonsoft.Json库,而是使用C#内置的功能进行反序列化。从.NETCore3.0开始,.NET提供了一个内置的JSON库:System.Text.Json。我们可以使用这个库来替代Newtonsoft.Json。主要步骤:1.使用`System.Text.Json.JsonSerializer.Deserialize<T>(stringjson)`方法进行反序列化。2.注意:内置库与Newtonsoft.Json在默认行为上可能有所不同,例如大小写策略、忽略空值等,可以通过设置选项(JsonSerializerOptions)来调整。示例:假设我们有一个类:```csharppublicclassPerson{publicstringName{get;set;}publicintAge{get;set;}}```反序列化:```csharpstringjson="{\"Name\":\"John\",\"Age\":30}";Personperson=System.Text.Json.JsonSerializer.Deserialize<Person>(json);```如果需要调整选项,例如允许属性名不区分大小写、忽略空值等:```csharpvaroptions=newJsonSerializerOptions{PropertyNameCaseInsensitive=true,//忽略属性名大小写IgnoreNullValues=true//忽略JSON中的空值(在.NET5及以后版本中,此属性已改为DefaultIgnoreCondition)//在.NET5及更高版本中,使用:DefaultIgnoreCondition=JsonIgnoreCondition.WhenWritingNull};Personperson=JsonSerializer.Deserialize<Person>(json,options);```对于多态反序列化System.Text.Json.NET5.0开始支持,通过`JsonDerivedType`特性(但需要显式注册派生类型)或者使用类型鉴别器(类似于Newtonsoft的TypeNameHandling,但默认不提供自动类型信息,需要手动处理)。在.NET7及更高版本中,对多态序列化的支持更完善。例如,在.NET7中,我们可以使用`[JsonDerivedType]`特性:```csharp[JsonDerivedType(typeof(Derived1),"derived1")][JsonDerivedType(typeof(Derived2),"derived2")]publicclassBaseClass{publicintId{get;set;}}publicclassDerived1:BaseClass{publicstringName{get;set;}}publicclassDerived2:BaseClass{publicboolFlag{get;set;}}```然后序列化/反序列化时,会使用类型鉴别器(即上面字符串"derived1"或"derived2")来区分类型。但是,如果使用较老的版本(如.NETCore3.x),则可能需要自己编写转换器(JsonConverter)来处理多态反序列化。另外,如果不想使用System.Text.Json,也可以考虑其他第三方库,如:-Utf8Json(性能较高)-Jil-或者使用DataContractJsonSerializer(较老的库,在System.Runtime.Serialization.Json中)但根据问题要求,我们主要使用内置功能,所以推荐System.Text.Json。因此,总结替代方案:1.使用System.Text.Json.NETCore3.0及以上)2.对于复杂场景(如多态),可能需要配置选项或自定义转换器。注意:在.NETFramework中,如果没有System.Text.Json(因为它是.NETCore3.0及以上引入的),则可以使用内置的JavaScriptSerializer(在System.Web.Script.Serialization中)或DataContractJsonSerializer(在System.Runtime.Serialization.Json中)。例如,使用JavaScriptSerializer(需要引用System.Web.Extensions):```csharpusingSystem.Web.Script.Serialization;varserializer=newJavaScriptSerializer();varobj=serializer.Deserialize<Person>(json);```使用DataContractJsonSerializer:```csharpusingSystem.Runtime.Serialization.Json;using(varstream=newMemoryStream(Encoding.UTF8.GetBytes(json))){varserializer=newDataContractJsonSerializer(typeof(Person));varobj=(Person)serializer.ReadObject(stream);}```但通常,如果使用的是较新的.NET版本,推荐使用System.Text.Json。因此,针对用户的问题,我们给出以下回答:</think>### C#使用 Newtonsoft.Json 的反序列化替代方案 在 C# 中,不使用 Newtonsoft.Json 进行反序列化的主要替代方案是使用 .NET 内置的 `System.Text.Json` 库(.NET Core 3.0+)或旧版框架中的 `DataContractJsonSerializer`。以下是具体实现方法: --- #### 方案一:使用 System.Text.Json(推荐) **适用平台:.NET Core 3.0+ / .NET 5+** ```csharp using System.Text.Json; // 定义数据模型 public class Person { public string Name { get; set; } public int Age { get; set; } } // 反序列化示例 string json = "{\"Name\":\"Alice\",\"Age\":30}"; // 基本反序列化 Person person = JsonSerializer.Deserialize<Person>(json); // 带配置的反序列化(如大小写不敏感) var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true // 忽略属性名大小写 }; Person person = JsonSerializer.Deserialize<Person>(json, options); ``` **关键特性**: 1. **高性能**:比 Newtonsoft.Json 快 2-3 倍 2. **默认安全**:不自动执行类型转换,防止注入攻击 3. **配置灵活**: - `PropertyNamingPolicy`:命名策略(如驼峰命名) - `IgnoreNullValues`:忽略空值 - `Converters`:自定义转换器 --- #### 方案二:使用 DataContractJsonSerializer(.NET Framework) **适用平台:.NET Framework 3.5+** ```csharp using System.Runtime.Serialization.Json; using System.IO; // 需添加 [DataContract] 特性 [DataContract] public class Person { [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } } // 反序列化方法 string json = "{\"Name\":\"Bob\",\"Age\":25}"; using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) { var serializer = new DataContractJsonSerializer(typeof(Person)); Person person = (Person)serializer.ReadObject(stream); } ``` --- #### 方案三:自定义简单解析器(仅适用于简单 JSON) ```csharp // 使用 System.Text.JsonJsonDocument(.NET Core 3.0+) using JsonDocument doc = JsonDocument.Parse(json); JsonElement root = doc.RootElement; string name = root.GetProperty("Name").GetString(); int age = root.GetProperty("Age").GetInt32(); ``` --- #### 多态反序列化实现(System.Text.Json) ```csharp [JsonDerivedType(typeof(Student), "student")] [JsonDerivedType(typeof(Teacher), "teacher")] public class Person { /* 基础属性 */ } public class Student : Person { /* 扩展属性 */ } public class Teacher : Person { /* 扩展属性 */ } // 反序列化时自动识别类型 var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, WriteIndented = true }; string json = "{\"$type\":\"teacher\",\"Subject\":\"Math\"}"; Person person = JsonSerializer.Deserialize<Person>(json, options); ``` --- #### 各方案对比 | **特性** | System.Text.Json | DataContractJsonSerializer | Newtonsoft.Json | |------------------------|------------------|----------------------------|----------------| | 性能 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | | 跨平台支持 | ✅ (.NET Core 3+) | ✅ (.NET Framework) | ✅ | | 无需第三方依赖 | ✅ | ✅ | ❌ | | 多态反序列化支持 | .NET 7+ | ❌ | ✅ | | 复杂自定义转换易用性 | ⭐⭐ | ⭐ | ⭐⭐⭐⭐ | > **建议**:对于新项目,优先使用 `System.Text.Json`;维护旧项目时,若无法升级 .NET 版本,可考虑 `DataContractJsonSerializer`[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hefeng_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值