当前位置: 首页 > article >正文

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。我们还探索了如何使用处理大小写敏感性和处理循环引用。

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


http://www.kler.cn/a/472755.html

相关文章:

  • 【CSS】设置滚动条样式
  • Selenium 的四种等待方式及使用场景
  • C++ 复习总结记录六
  • flink cdc oceanbase(binlog模式)
  • 基于SMT32U575RIT单片机-中断练习
  • Elasticsearch:优化的标量量化 - 更好的二进制量化
  • ffplay 命令行 从视频第N帧开始读取 ffmpeg 命令行 提取第N帧图片
  • Omnivore 替代品 Readeck 安装与使用教程
  • (k8s)Flannel Error问题解决!
  • LeetCode【剑指offer】系列(字符串篇)
  • 使用葡萄城+vue实现Excel
  • 代码填空任务---自编码器模型
  • vue2迁移至rsbuild
  • Github Copilot学习笔记
  • 【大模型】百度千帆大模型对接LangChain使用详解
  • vue3运行时执行过程步骤
  • 如何写一个uniapp自定义tarbar导航栏?
  • 联邦学习中的LoRA:FedLoRA
  • Gin 框架中间件原理
  • 小程序开发-页面事件之上拉触底实战案例
  • Win32汇编学习笔记07.筛选器异常
  • nginx-配置指令的执行顺序!
  • Dart语言的网络编程
  • React中 Reconciliation算法详解
  • 深度学习blog-深刻理解线性变换和矩阵
  • 负载均衡技术【内网去外网运营商出口负载均衡】