C# .NetCore 中使用 System.Text.Json 序列化 JSON 示例
自定义和控制 JSON 输出,实现JsonSerializerOptions
更清洁、高效的数据处理。
介绍
在这篇文章中,我将深入研究如何使用System.Text.Json
库在 C# 中进行 JSON 序列化。我将介绍如何序列化和反序列化对象、处理空值等特殊情况、格式化日期以及使用自定义输出JsonSerializerOptions
。
导入必要的命名空间。这包括System.Text.Json
JSON 处理和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。我们还探索了如何使用处理大小写敏感性和处理循环引用。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。