视频地址:分享一些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可以与其他压缩算法结合使用,以进一步减少存储空间。