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

[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可以与其他压缩算法结合使用,以进一步减少存储空间。


http://www.kler.cn/news/309200.html

相关文章:

  • 中秋节特别游戏:给玉兔投喂月饼
  • MinIO - macOS上配置、Python调用
  • Delphi Web和Web服务开发目前有哪些选择
  • ASP.NET Core 中的 CRUD 操作
  • 游戏录屏不清楚怎么办?这些录屏技巧让画质飙升!
  • 标准的高防服务器是什么样的呢?
  • VSCode扩展连接虚拟机MySQL数据库
  • 【JavaScript】数据结构之堆
  • Python 数学建模——ARMA 时间序列分析
  • Unite Shanghai 2024 技术专场 | Unity 6及未来规划:Unity引擎和服务路线图
  • docker_持久化存储
  • 如何在Linux云服务器上解决网站截图问题
  • 【PyQt5】QWidget子类所有子类
  • 佰朔资本:股票中什么叫龙头?怎么找龙头股?
  • linux-系统管理与监控-设备管理
  • Linux 常用指令
  • 数据结构-链式二叉树-四种遍历
  • unity关于UI隐藏时性能问题
  • 解决Tez报错问题
  • 堆的概念与实现
  • 【QT】使用QOpenGLWidget后,窗口全屏之后右键菜单出不来的问题
  • java多线程模拟多个售票员从同一个票池售票
  • c++面试-语法糖(一)
  • 54. 二叉搜索树的第 k 大节点
  • 09年408考研真题-数据结构
  • MATLAB|基于多时段动态电价的电动汽车有序充电策略优化
  • 【Qt】实现模拟触摸屏 上下滑动表格 的两种方式
  • 产品经理学AI:搭建大模型应用常用的三种方式
  • 【我的 PWN 学习手札】Fastbin Attack
  • TVM和EVM的比较