[C#学习笔记]Newtonsoft.Json
视频地址:分享一些Newtonsoft.Json的实用功能与技巧_哔哩哔哩_bilibili
强烈推荐学习C#和WPF的朋友关注此UP,知识点巨多,讲解透彻!
一、JsonSerializerSettings
1.1 自动缩进-Formatting
使用Formatting.None进行序列化
var s = new Student
{
PersonId = 1,
FullName = "Jack Doe",
};
var setting = new JsonSerializerSettings
{
Formatting = Formatting.None,
};
var r = JsonConvert.SerializeObject(s, setting);
Console.WriteLine(r);
class Student
{
public int PersonId { get; set; }
public string FullName { get; set; }
}
输出结果为
{"PersonId":1,"FullName":"Jack Doe"}
改为Formatting.None后,输出结果为
{
"PersonId": 1,
"FullName": "Jack Doe"
}
1.2 命名策略-NamingStrategy
1.2.3 蛇形命名-SnakeCaseNamingStrategy
将设置改成
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};
输出结果为
{
"person_id": 1,
"full_name": "Jack Doe"
}
1.2.4 烤串命名-KebabCaseNamingStrategy
{
"person-id": 1,
"full-name": "Jack Doe"
}
1.2.5 驼峰命名-CamelCaseNamingStrategy
{
"personId": 1,
"fullName": "Jack Doe"
}
1.2.6 帕斯卡命名-DefaultNamingStrategy
{
"PersonId": 1,
"FullName": "Jack Doe"
}
1.3 空值处理- NullValueHandling
1.3.1 忽略-Ignore
实例对象未对可空属性Address赋值,序列化属性使用NullValueHandling.Ignore
var s = new Student
{
PersonId = 1,
FullName = "Jack Doe",
};
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new DefaultNamingStrategy()
},
NullValueHandling = NullValueHandling.Ignore,
};
var r = JsonConvert.SerializeObject(s, setting);
Console.WriteLine(r);
class Student
{
public int PersonId { get; set; }
public string? Address { get; set; }
public string FullName { get; set; }
}
输出结果,可以看到输出结果中也没有Address属性
{
"PersonId": 1,
"FullName": "Jack Doe"
}
1.3.2 包含-Include
改为NullValueHandling.Include后,输出结果
{
"PersonId": 1,
"Address": null,
"FullName": "Jack Doe"
}
1.4 字符串转义-StringEscapeHandling
1.4.1 仅限Ascii-EscapeNonAscii
var s = new Student
{
PersonId = 1,
FullName = "Jack Doe",
Address = "中国"
};
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new DefaultNamingStrategy()
},
NullValueHandling = NullValueHandling.Include,
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
};
var r = JsonConvert.SerializeObject(s, setting);
Console.WriteLine(r);
class Student
{
public int PersonId { get; set; }
public string? Address { get; set; }
public string FullName { get; set; }
}
输出结果
{
"PersonId": 1,
"Address": "\u4e2d\u56fd",
"FullName": "Jack Doe"
}
1.4.2 默认-Default
{
"PersonId": 1,
"Address": "中国",
"FullName": "Jack Doe"
}
1.5 类型名称-TypeNameHandling
将设置改成
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new DefaultNamingStrategy()
},
NullValueHandling = NullValueHandling.Include,
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii,
TypeNameHandling = TypeNameHandling.All,
};
再进行序列化
{
"$type": "Student, ConsoleApp2",
"PersonId": 1,
"Address": "中国",
"FullName": "Jack Doe"
}
可以看到多了一个$Type属性,内容为类名和命名空间
以下为其一个应用场景
一个抽象类Person,类Student和Employee继承自Person
abstract class Person
{
public string Name { get; set; }
public string Phone { get; set; }
}
class Student:Person
{
public string School { get; set; }
public int Grade { get; set; }
}
class Employee : Person
{
public string Department { get; set; }
public double Salary { get; set; }
}
进行实例化
var people = new List<Person>()
{
new Student
{
Name = "John Doe",
Phone = "123-456-789",
School = "School of Hard Knocks",
Grade = 12
},
new Employee
{
Name = "Jack Doe",
Phone = "987-654-321",
Department = "Engineering",
Salary = 10000
}
};
使用以下设置进行序列化
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.All,
};
输出结果为
{
"$type": "System.Collections.Generic.List`1[[Person, ConsoleApp2]], System.Private.CoreLib",
"$values": [
{
"$type": "Student, ConsoleApp2",
"School": "School of Hard Knocks",
"Grade": 12,
"Name": "John Doe",
"Phone": "123-456-789"
},
{
"$type": "Employee, ConsoleApp2",
"Department": "Engineering",
"Salary": 10000.0,
"Name": "Jack Doe",
"Phone": "987-654-321"
}
]
}
将TypeNameHandling改为Auto后输出结果为
[
{
"$type": "Student, ConsoleApp2",
"School": "School of Hard Knocks",
"Grade": 12,
"Name": "John Doe",
"Phone": "123-456-789"
},
{
"$type": "Employee, ConsoleApp2",
"Department": "Engineering",
"Salary": 10000.0,
"Name": "Jack Doe",
"Phone": "987-654-321"
}
]
可以看到TypeNameHandling.All会输出所有的类型名,而TypeNameHandling.Auto只会在可能产生冲突或者有需要的位置添加类型名
接下来,将TypeNameHandling改成None,输出结果为
[
{
"School": "School of Hard Knocks",
"Grade": 12,
"Name": "John Doe",
"Phone": "123-456-789"
},
{
"Department": "Engineering",
"Salary": 10000.0,
"Name": "Jack Doe",
"Phone": "987-654-321"
}
]
使用以下设置进行反序列化
var source = """
[
{
"School": "School of Hard Knocks",
"Grade": 12,
"Name": "John Doe",
"Phone": "123-456-789"
},
{
"Department": "Engineering",
"Salary": 10000.0,
"Name": "Jack Doe",
"Phone": "987-654-321"
}
]
""";
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.None,
};
var result = JsonConvert.DeserializeObject<List<Person>>(source, setting);
运行时报错,提示抽象类和接口不能进行反序列化
将TypeNameHandling改为Auto,进行序列化,输出结果
[
{
"$type": "Student, ConsoleApp2",
"School": "School of Hard Knocks",
"Grade": 12,
"Name": "John Doe",
"Phone": "123-456-789"
},
{
"$type": "Employee, ConsoleApp2",
"Department": "Engineering",
"Salary": 10000.0,
"Name": "Jack Doe",
"Phone": "987-654-321"
}
]
使用此内容和设置进行反序列化
var source = """
[
{
"$type": "Student, ConsoleApp2",
"School": "School of Hard Knocks",
"Grade": 12,
"Name": "John Doe",
"Phone": "123-456-789"
},
{
"$type": "Employee, ConsoleApp2",
"Department": "Engineering",
"Salary": 10000.0,
"Name": "Jack Doe",
"Phone": "987-654-321"
}
]
""";
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.Auto,
};
var result = JsonConvert.DeserializeObject<List<Person>>(source, setting);
return;
可以看到没有报错,对result进行监视可以看到其内容,成功进行了反序列化
1.6 对象创建策略-ObjectCreationHandling
定义一个类
class Person
{
public string Name { get; set; }
public List<string> Hobbies { get; set; }
}
对其进行实例化
var p = new Person
{
Name = "John Doe",
Hobbies = ["Chess", "Tennis"]
};
定义一个JsonSerializerSettings
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ObjectCreationHandling = ObjectCreationHandling.Auto,
};
对p进行序列后生成
{
"Name": "John Doe",
"Hobbies": [
"Chess",
"Tennis"
]
}
现在修改p的内容为
var p = new Person
{
Name = "John Doe",
Hobbies = ["Swimming", "Reading"]
};
定义一个字符串data
var data = """
{
"Name": "John Doe",
"Hobbies": [
"Chess",
"Tennis"
]
}
""";
调用PopulateObject方法
JsonConvert.PopulateObject(json, p, setting);
得到p的结果为
ObjectCreationHandling.Auto使得字符串中的两个元素追加到对象p的集合Hobbies中
现在将ObjectCreationHandling修改为Replace,再调用方法,得到
ObjectCreationHandling.Replace使得 字符串中的两个元素覆盖了对象p的集合Hobbies中的元素
二、常用特性
代码:
var p = new Person
{
Name = "Jack",
};
Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented));
class Person
{
private readonly DateTime CreateTime = DateTime.Now;
public string Name { get; set; }
public string Description => $"{Name} Created at {CreateTime}";
}
序列化后生成
{
"Name": "Jack",
"Description": "Jack Created at 2024/9/12 20:18:34"
}
2.1 类特性与JsonSerializerSettings
类可以添加特性(包含JsonSerializerSettings中的所有参数),针对整个类生效
2.2 添加标记-JsonProperty
[JsonProperty()]
private readonly DateTime CreateTime = DateTime.Now;
序列后后生成
{
"CreateTime": "2024-09-12T20:19:40.781696+08:00",
"Name": "Jack",
"Description": "Jack Created at 2024/9/12 20:19:40"
}
还可对序列化后的字段名称进行定制
[JsonProperty("Time")]
private readonly DateTime CreateTime = DateTime.Now;
序列化后生成
{
"Time": "2024-09-12T20:20:12.251123+08:00",
"Name": "Jack",
"Description": "Jack Created at 2024/9/12 20:20:12"
}
2.3 忽略-JsonIgnore
[JsonIgnore]
public string Description => $"{Name} Created at {CreateTime}";
序列化后生成
{
"Time": "2024-09-12T20:22:03.0953351+08:00",
"Name": "Jack"
}
2.4 成员序列化方式-MemberSerialization
2.4.1 Fields
[JsonObject(MemberSerialization.Fields)]
class Person
{
private readonly DateTime CreateTime = DateTime.Now;
public string Name { get; set; }
public string Description => $"{Name} Created at {CreateTime}";
}
序列化后生成
{
"CreateTime": "2024-09-12T20:24:55.9249168+08:00",
"<Name>k__BackingField": "Jack"
}
可以看到只对字段进行序列化
2.4.2 OptIn
[JsonObject(MemberSerialization.OptIn)]
class Person
{
[JsonProperty]
private readonly DateTime CreateTime = DateTime.Now;
public string Name { get; set; }
public string Description => $"{Name} Created at {CreateTime}";
}
序列后生成
{
"CreateTime": "2024-09-12T20:26:20.8093154+08:00"
}
只对有特性JsonProperty的成员进行序列化
2.4.3 OptOut
[JsonObject(MemberSerialization.OptOut)]
class Person
{
private readonly DateTime CreateTime = DateTime.Now;
public string Name { get; set; }
public string Description => $"{Name} Created at {CreateTime}";
}
序列后后生成
{
"Name": "Jack",
"Description": "Jack Created at 2024/9/12 20:28:07"
}
只要未标记JsonIgnore的属性都将进行序列化
2.4.4 应用场景
var p = new Person
{
Name = "Jack",
};
Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented));
abstract class Human
{
public bool IsAlive { get; set; } = true;
}
class Person : Human
{
private readonly DateTime CreateTime = DateTime.Now;
public string Name { get; set; }
public string Description => $"{Name} Created at {CreateTime}";
}
类Person继承自Human,Human中有一个属性IsAlive,序列化后生成
{
"Name": "Jack",
"Description": "Jack Created at 2024/9/12 20:30:16",
"IsAlive": true
}
现在假设生成的Json中不需要IsAlive字段,Human又不可修改(比如说来自标准库或第三方库),此时就可以用到MemberSerialization特性,修改代码
abstract class Human
{
public bool IsAlive { get; set; } = true;
}
[JsonObject(MemberSerialization.OptIn)]
class Person : Human
{
[JsonProperty]
private readonly DateTime CreateTime = DateTime.Now;
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Description => $"{Name} Created at {CreateTime}";
}
序列化后生成
{
"CreateTime": "2024-09-12T20:33:57.3095584+08:00",
"Name": "Jack",
"Description": "Jack Created at 2024/9/12 20:33:57"
}
三、 转换器-JsonConverter
3.1 枚举-字符串-StringEnumConverter
var p = new Person
{
Name = "Jack",
Gender = Gender.Male
};
var json = JsonConvert.SerializeObject(p, Formatting.Indented);
Console.WriteLine(json);
class Person
{
public string Name { get; set; }
public Gender Gender { get; set; }
}
enum Gender
{
Male,
Female,
}
序列后生成
{
"Name": "Jack",
"Gender": 0
}
添加特性
[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }
序列后生成
{
"Name": "Jack",
"Gender": "Male"
}
3.2 自定义转换器1
var p = new Person
{
Name = "Jack",
Birthday = new DateTime(1990, 1, 1),
};
var json = JsonConvert.SerializeObject(p, Formatting.Indented);
Console.WriteLine(json);
class Person
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
序列化后生成
{
"Name": "Jack",
"Birthday": "1990-01-01T00:00:00"
}
如果生成的Birthday字段只需要年月日,可修改代码为
var p = new Person
{
Name = "Jack",
Birthday = new DateTime(1990, 1, 1),
};
var json = JsonConvert.SerializeObject(p, Formatting.Indented);
Console.WriteLine(json);
class Person
{
public string Name { get; set; }
[JsonConverter(typeof(MyDateTimeConverter))]
public DateTime Birthday { get; set; }
}
class MyDateTimeConverter : IsoDateTimeConverter
{
public MyDateTimeConverter()
{
DateTimeFormat = "yyyy-MM-dd";
}
}
序列化后生成
{
"Name": "Jack",
"Birthday": "1990-01-01"
}
3.3 自定义转换器2
var p = new Person
{
Name = "Jack",
Age = new IntWrapper(30)
};
var json = JsonConvert.SerializeObject(p, Formatting.Indented);
Console.WriteLine(json);
class Person
{
public string Name { get; set; }
public IntWrapper Age { get; set; }
}
class IntWrapper
{
public IntWrapper(int value)
{
Value = value;
}
public int Value { get; set; }
}
Age属性进行了封装,序列化后生成
{
"Name": "Jack",
"Age": {
"Value": 30
}
}
如果想生成"Age":30,可以自定义转换器,如下
var p = new Person
{
Name = "Jack",
Age = new IntWrapper(30)
};
var json = JsonConvert.SerializeObject(p, Formatting.Indented);
Console.WriteLine(json);
class Person
{
public string Name { get; set; }
[JsonConverter(typeof(IntWrapperConverter))]
public IntWrapper Age { get; set; }
}
class IntWrapper
{
public IntWrapper(int value)
{
Value = value;
}
public int Value { get; set; }
}
class IntWrapperConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IntWrapper).IsAssignableFrom(objectType);
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
return new IntWrapper(Convert.ToInt32(reader.Value));
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
serializer.Serialize(writer, ((IntWrapper)value).Value);
}
}
序列后生成
{
"Name": "Jack",
"Age": 30
}
四、回调函数
4.1 序列化中
[OnSerializing]
internal void OnSerializingMethod(StreamingContext context)
{
}
4.2 序列化后
[OnSerialized]
internal void OnSerializedMethod(StreamingContext context)
{
}
4.3 反序列化中
[OnDeserializing]
internal void OnDeserializingMethod(StreamingContext context)
{
}
4.4 反序列后
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
}
4.5 例程
var p = new Person
{
Name = "Jack",
Age = 30,
};
var json = JsonConvert.SerializeObject(p, Formatting.Indented);
Console.WriteLine(json);
var result = JsonConvert.DeserializeObject<Person>(json);
Console.WriteLine(result);
class Person
{
public string Name { get; set; }
public int Age { get; set; }
[OnSerializing]
internal void OnSerializingMethod(StreamingContext context)
{
Console.WriteLine("开始序列化.");
}
[OnSerialized]
internal void OnSerializedMethod(StreamingContext context)
{
Console.WriteLine("序列化结束.");
}
[OnDeserializing]
internal void OnDeserializingMethod(StreamingContext context)
{
Console.WriteLine("开始反序列化.");
}
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
Console.WriteLine("反序列化结束.");
}
}
class IntWrapper
{
public IntWrapper(int value)
{
Value = value;
}
public int Value { get; set; }
}
输出结果
开始序列化.
序列化结束.
{
"Name": "Jack",
"Age": 30
}
开始反序列化.
反序列化结束.
Person
五、未知对象的反序列化
var json = """
{
"user": {
"id": 1,
"name": "John Doe",
"contact": {
"email": "john.doe@example.com",
"phone": {
"home": "123-456-7890",
"mobile": "987-654-3210"
}
},
"address": {
"street": "123 Main St",
"city": "Anytown",
"postalCode": {
"code": "12345",
"extension": "6789"
}
}
}
}
""";
var obj = JObject.Parse(json);;
Console.WriteLine(obj["user"]["contact"]["email"]);
输出结果
john.doe@example.com
六、未定义类的序列化
var obj = new JObject(
new JProperty(
"user1",
new JObject(
new JProperty("id", 1),
new JProperty("name", "John Doe"),
new JProperty(
"contact",
new JObject(
new JProperty("email", "john.doe@example.com"),
new JProperty(
"phone",
new JObject(
new JProperty("home", "123-456-7890"),
new JProperty("mobile", "987-654-3210"))
)
)
)
)
)
);
Console.WriteLine(obj.ToString());
输出结果
{
"user1": {
"id": 1,
"name": "John Doe",
"contact": {
"email": "john.doe@example.com",
"phone": {
"home": "123-456-7890",
"mobile": "987-654-3210"
}
}
}
}
七、快速生成json字符串
使用匿名类
var obj = new
{
Name = "John",
Age = 30,
Class = 10,
};
Console.WriteLine(JsonConvert.SerializeObject(obj, Formatting.Indented));
输出结果
{
"Name": "John",
"Age": 30,
"Class": 10
}
八、 Bson
8.1存取效率
1.二进制格式:BSON 是一种二进制格式,解析速度通常比 JSON 的文本格式更快。这是因为解析二进制数据通常比解析文本数据更高效。
2.数据类型支持:BSON 支持更多的数据类型(例如日期、浮点数、整数等),而 JSON 则只有字符串、数字、布尔值和数组等基础类型。对于复杂的数据结构,BSON 可以直接存储和处理,而 JSON 可能需要额外的编码和解码操作。
3.内嵌文档和数组:BSON 允许文档和数组直接嵌套在二进制数据中,无需像 JSON 那样进行字符编码和解码,因而可以加快数据存取速度。
8.2 空间利用率
1.数据类型标识:BSON 使用数据类型标识符来明确每个字段的数据类型,从而避免了 JSON 中冗余的字符(例如引号和冒号等)。这在一定程度上可以减少存储空间。
2.字符串长度前缀:BSON 存储字符串时会在前面加上长度前缀,使得读取和写入字符串时不需要逐字符解析,可以提高效率并减少空间开销。
3.压缩:虽然 BSON 本身不是压缩格式,但其二进制表示形式在某些情况下可能比 JSON 更紧凑。此外,BSON可以与其他压缩算法结合使用,以进一步减少存储空间。