您的位置:首页 > 汽车 > 新车 > [C#学习笔记]Newtonsoft.Json

[C#学习笔记]Newtonsoft.Json

2024/11/17 17:32:28 来源:https://blog.csdn.net/wo6370431/article/details/142156713  浏览:    关键词:[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可以与其他压缩算法结合使用,以进一步减少存储空间。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com