您的位置:首页 > 手游攻略 > C#常用类库Newtonsoft.Json的实现方式示例实用指南

C#常用类库Newtonsoft.Json的实现方式示例实用指南

作者:互联网  时间: 2026-11-01 08:50:02  

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“C#常用类库Newtonsoft.Json的实现方法示例”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

今天咱们深入拆解.NET生态中最常用、最经典的JSON处理类库——Newtonsoft.Json从实现思路看,(俗称Json.NET)。作为.NET开发者,JSON序列化与反序列化是日常开发中高频场景(接口调用、设置解析、数据存储等),而Newtonsoft.Json凭借其强大的功能、良好的兼容性、灵活的设置以及成熟的生态,长期占据.NET JSON处理领域的主导地位,甚至在.NET Core/.NET 5+时代,即便微软推出了System.Text.Json,它依然是很多项目的首选。

本文不会只停留在“如何序列化/反序列化”的基础用法,而是从基础用法→核心设置→高级特性→底层原理→性能优化→实际避坑落到代码里,,全方位、有深度地解析Newtonsoft.Json,帮你从“会用”提升到“精通”,解决真实开发场景下遇到的复杂JSON处理问题,同时理解其背后的设计逻辑,面试中遇到相关问题也能从容应对。

一、前言:Newtonsoft.Json的定位与优势

结合项目来看,在正式讲解用法之前,我们先明确Newtonsoft.Json的核心定位和优势——为什么它能成为.NET开发者的首选,以及它与微软自带的System.Text.Json的核心差异(这也是面试高频考点)。

1. 核心定位

理解这一步时,Newtonsoft.Json是一个高性能、灵活的JSON处理类库,兼容.NET Framework 2.0+、.NET Core、.NET 5+、Xamarin、Unity等几乎所有.NET平台,核心功能是实现JSON与.NET对象之间的相互转换(序列化:.NET对象→JSON字符串;反序列化:JSON字符串→.NET对象),同时提供了丰富的设置选项和扩展能力,满足复杂场景下的JSON处理需求。

2. 核心优势(对比System.Text.Json)

理解这一步时,微软在.NET Core 3.0中推出了System.Text.Json,作为官方JSON处理类库,主打高性能和原生兼容,但Newtonsoft.Json依然有不可替代的优势,尤其在老项目和复杂场景中:

  • 兼容性极强:兼容所有.NET框架版本,包括老旧的.NET Framework 2.0/3.5,而System.Text.Json仅兼容.NET Core 3.0+、.NET 5+,对老项目不友好。
  • 功能更全面:提供了更多高级特性(如自定义转换器、条件序列化、引用循环处理、动态JSON处理等),System.Text.Json的很多高级功能都是后期借鉴Newtonsoft.Json补充的。
  • 灵活性更高:设置选项更丰富,可借助JsonSerializerSettings精细控制序列化/反序列化的每一个环节,适配各种复杂JSON结构。
  • 生态更成熟:长期迭代,bug修复更完善,社区资源丰富,遇到问题能更快找到解决方案;很多第三方类库、框架(如ASP.NET MVC、Entity Framework)默认集成Newtonsoft.Json。

补充:.NET 5+项目中,可根据需求选择——追求极致性能且场景轻松,选System.Text.Json;需复杂设置、兼容性或依赖第三方生态,选Newtonsoft.Json。本文重点聚焦Newtonsoft.Json的深度解析。

二、基础用法:从零开始采用Newtonsoft.Json

结合项目来看,先掌握基础用法,这是后续深度应用的前提。主要分为“安装类库→核心API调用→轻松序列化/反序列化”三个步骤,示例可直接复制运行。

1. 安装Newtonsoft.Json

最常用的方式是借助NuGet安装(Visual Studio中):

  • 方式1:NuGet包管理器搜索“Newtonsoft.Json”,点击安装(建议最新稳定版)。
  • 方式2:Package Manager Console输入命令:Install-Package Newtonsoft.Json。
  • 方式3:.NET CLI输入命令:dotnet add package Newtonsoft.Json。

结合项目来看,安装完成后,在代码中引入命名空间:using Newtonsoft.Json;,即可采用其核心API。

2. 核心API介绍

落到代码里,Newtonsoft.Json的核心API主要集中在两个类中,用法简洁,覆盖绝大多数基础场景:

  • JsonConvert:静态类,提供静态方法,适用来大多数轻松场景,无需新建实例,调用便捷。
  • JsonSerializer:实例类,提供更灵活的设置和控制,适用来复杂场景(如大文件序列化、自定义设置持久化等)。

核心方法(序列化/反序列化):

  • 序列化:JsonConvert.SerializeObject(object value) → 将.NET对象转为JSON字符串。
  • 反序列化:JsonConvert.DeserializeObject(string json) → 将JSON字符串转为指定类型的.NET对象。

3. 基础示例(覆盖高频场景)

结合项目来看,我们借助“轻松对象、集合、泛型、匿名对象”四个场景,演示基础用法,结合注释理解。

示例1:轻松对象的序列化与反序列化

// 1. 定义一个实体类(模拟实际开发中的DTO、模型)
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public DateTime CreateTime { get; set; }
    public bool IsActive { get; set; }
}

// 2. 序列化:.NET对象 → JSON字符串
User user = new User
{
    Id = 1,
    Name = "张三",
    Age = 25,
    CreateTime = DateTime.Now,
    IsActive = true
};
// 基础序列化
string json = JsonConvert.SerializeObject(user);
Console.WriteLine("基础序列化结果:");
Console.WriteLine(json);
// 输出(默认格式,带转义符,日期为ISO格式):
// {"Id":1,"Name":"张三","Age":25,"CreateTime":"2024-05-20T15:30:00","IsActive":true}

// 3. 反序列化:JSON字符串 → .NET对象
User deserializedUser = JsonConvert.DeserializeObject<User>(json);
Console.WriteLine("n反序列化结果:");
Console.WriteLine($"Id: {deserializedUser.Id}, Name: {deserializedUser.Name}, Age: {deserializedUser.Age}");

示例2:集合、泛型的序列化与反序列化

// 集合序列化(List<User>)
List<User> userList = new List<User>
{
    new User { Id = 1, Name = "张三", Age = 25 },
    new User { Id = 2, Name = "李四", Age = 28 }
};
string jsonList = JsonConvert.SerializeObject(userList);
Console.WriteLine("集合序列化结果:");
Console.WriteLine(jsonList);
// 输出:[{"Id":1,"Name":"张三","Age":25,"CreateTime":"0001-01-01T00:00:00","IsActive":false},{"Id":2,"Name":"李四","Age":28,"CreateTime":"0001-01-01T00:00:00","IsActive":false}]

// 泛型反序列化(List<User>)
List<User> deserializedList = JsonConvert.DeserializeObject<List<User>>(jsonList);
foreach (var u in deserializedList)
{
    Console.WriteLine($"Id: {u.Id}, Name: {u.Name}");
}

// 字典序列化(Dictionary<string, object>)
Dictionary<string, object> dict = new Dictionary<string, object>
{
    { "Id", 1 },
    { "Name", "张三" },
    { "Age", 25 }
};
string jsonDict = JsonConvert.SerializeObject(dict);
Console.WriteLine("n字典序列化结果:");
Console.WriteLine(jsonDict);

示例3:匿名对象的序列化(无需定义实体类)

// 匿名对象(适用于临时数据,无需创建实体类)
var anonymousObj = new
{
    Id = 1,
    Name = "张三",
    Address = new { Province = "广东", City = "深圳" }, // 嵌套匿名对象
    Hobbies = new List<string> { "篮球", "游戏" }
};

// 序列化匿名对象
string jsonAnonymous = JsonConvert.SerializeObject(anonymousObj);
Console.WriteLine("匿名对象序列化结果:");
Console.WriteLine(jsonAnonymous);
// 输出:{"Id":1,"Name":"张三","Address":{"Province":"广东","City":"深圳"},"Hobbies":["篮球","游戏"]}

// 反序列化匿名对象(需指定匿名对象模板)
var deserializedAnonymous = JsonConvert.DeserializeAnonymousType(jsonAnonymous, new
{
    Id = 0,
    Name = "",
    Address = new { Province = "", City = "" },
    Hobbies = new List<string>()
});
Console.WriteLine("n匿名对象反序列化结果:");
Console.WriteLine($"Name: {deserializedAnonymous.Name}, Province: {deserializedAnonymous.Address.Province}");

【关键提醒】:匿名对象反序列化时,必须提供“模板”(第二个参数),且模板的属性名、类型必须与JSON字符串一致,否则无法正确反序列化。

三、核心设置:JsonSerializerSettings详解(重点)

结合项目来看,基础用法只能满足轻松场景,真实开发场景下,我们经常需控制JSON的格式(如美化、日期格式)、忽略某些属性、处理空值、解决引用循环等,这就需用到JsonSerializerSettings实际处理时,——Newtonsoft.Json的核心设置类,借助它能够精细控制序列化/反序列化的每一个环节,也是体现其灵活性的关键。

核心用法:在SerializeObject/DeserializeObject方法中,传入第二个参数(JsonSerializerSettings实例),即可应用设置。下面我们拆解最常用、最实用的设置项,结合示例说明其作用和场景。

1. 格式化设置(Formatting)

控制JSON字符串的格式,常用两个值,适用来不同场景:

  • Formatting.None:默认值,紧凑格式,无换行、无缩进,适合网络传输(节省带宽)。
  • Formatting.Indented:美化格式,有换行、缩进,适合调试、日志输出(可读性强)。
// 美化格式序列化
string prettyJson = JsonConvert.SerializeObject(user, Formatting.Indented);
Console.WriteLine("美化格式JSON:");
Console.WriteLine(prettyJson);
// 输出(有缩进、换行,可读性强):
// {
// "Id": 1,
// "Name": "张三",
// "Age": 25,
// "CreateTime": "2024-05-20T15:30:00",
// "IsActive": true
// }

2. 日期格式化(DateFormatHandling、DateFormatString)

从实现思路看,JSON中日期的默认格式是ISO 8601(如"2024-05-20T15:30:00"),但真实开发场景下,我们常需自定义日期格式(如"yyyy-MM-dd HH:mm:ss"),这就需设置日期相关选项。

var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    // 日期处理方式:默认IsoDateFormat,可改为MicrosoftDateFormat("/Date(123456789)/"格式)
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    // 自定义日期格式(最常用)
    DateFormatString = "yyyy-MM-dd HH:mm:ss"
};

string jsonWithDate = JsonConvert.SerializeObject(user, settings);
Console.WriteLine("自定义日期格式JSON:");
Console.WriteLine(jsonWithDate);
// 输出:CreateTime的值变为 "2024-05-20 15:30:00"

【补充】:若需将日期转为时间戳(毫秒),可结合自定义转换器,后续高级特性中讲解。

3. 空值处理(NullValueHandling)

从实现思路看,控制JSON中是否包含“值为null”的属性,常用场景:接口得到时,隐藏null属性,减少传输数据量。

  • NullValueHandling.Include:默认值,包含所有null属性。
  • NullValueHandling.Ignore:忽略所有null属性,不序列化到JSON中。
// 模拟一个包含null属性的对象
User userWithNull = new User
{
    Id = 3,
    Name = null, // null属性
    Age = 0,
    CreateTime = DateTime.Now,
    IsActive = false
};

var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    NullValueHandling = NullValueHandling.Ignore // 忽略null属性
};

string jsonWithoutNull = JsonConvert.SerializeObject(userWithNull, settings);
Console.WriteLine("忽略null属性的JSON:");
Console.WriteLine(jsonWithoutNull);
// 输出:Name属性被忽略,不显示在JSON中

4. 属性命名策略(ContractResolver)

落到代码里,控制JSON属性名的格式,最常用场景:.NET实体类用帕斯卡命名法(PascalCase,如UserName),而接口要求JSON用驼峰命名法(camelCase,如userName),借助设置可自动转换,无需修改实体类。

var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    // 驼峰命名策略:将PascalCase转为camelCase
    ContractResolver = new CamelCasePropertyNamesContractResolver()
};

string jsonCamelCase = JsonConvert.SerializeObject(user, settings);
Console.WriteLine("驼峰命名JSON:");
Console.WriteLine(jsonCamelCase);
// 输出:属性名变为id、name、age、createTime、isActive

【补充】:可自定义ContractResolver,实现更复杂的命名策略(如下所示划线命名、全大写命名),后续高级特性中讲解。

5. 忽略指定属性(JsonIgnore特性)

除了借助NullValueHandling忽略null属性,还能够借助JsonIgnore特性结合项目来看,,强制忽略某个属性(无论其值是否为null),适用来“永远不需序列化”的属性(如密码、敏感信息)。

// 给实体类的敏感属性添加JsonIgnore特性
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    [JsonIgnore] // 强制忽略该属性,不序列化
    public string Password { get; set; } // 敏感信息,不对外暴露
}

// 序列化时,Password属性会被忽略
User userWithPwd = new User
{
    Id = 1,
    Name = "张三",
    Password = "123456"
};
string jsonIgnore = JsonConvert.SerializeObject(userWithPwd, Formatting.Indented);
Console.WriteLine("忽略敏感属性的JSON:");
Console.WriteLine(jsonIgnore);
// 输出:无Password属性

6. 引用循环处理(ReferenceLoopHandling)

从实现思路看,这是真实开发场景下高频遇到的问题:当两个对象相互引用(如User和Order,User包含Order列表,Order包含User引用),序列化时会出现“引用循环”,导致报错。Newtonsoft.Json提供了三种处理方式:

  • ReferenceLoopHandling.Error:默认值,遇到引用循环时抛出异常(InvalidOperationException)。
  • ReferenceLoopHandling.Ignore:忽略引用循环,序列化时不序列化循环引用的属性。
  • ReferenceLoopHandling.Serialize:序列化引用循环,会在JSON中添加ref(引用标识)和ref(引用标识)和ref(引用标识)和id(对象标识),用来表示引用关系。
// 模拟引用循环:User和Order相互引用
public class Order
{
    public int OrderId { get; set; }
    public User User { get; set; } // 引用User
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Order> Orders { get; set; } // 引用Order列表
}

// 构建引用循环
User user = new User { Id = 1, Name = "张三" };
Order order = new Order { OrderId = 1001, User = user };
user.Orders = new List<Order> { order }; // 引用循环:user→order→user

// 配置引用循环处理
var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore // 忽略引用循环
};

// 序列化不会报错,Order中的User属性会被忽略
string jsonNoLoop = JsonConvert.SerializeObject(user, settings);
Console.WriteLine("忽略引用循环的JSON:");
Console.WriteLine(jsonNoLoop);

7. 其他常用设置(补充)

  • DefaultValueHandling:控制默认值属性的序列化(如int=0、bool=false),可设置为Ignore忽略默认值。
  • TypeNameHandling:序列化时包含类型信息(如$type),适用来反序列化时需明确类型的场景(如抽象类、接口类型)。
  • StringEscapeHandling:控制字符串的转义(如中文、特殊字符),默认EscapeNonAscii(转义非ASCII字符),可设置为Default取消转义。

四、高级特性:解锁Newtonsoft.Json的强大能力(深度重点)

理解这一步时,基础设置能满足大部分场景,但在复杂场景(如自定义JSON结构、处理特殊类型、动态JSON、性能优化)中,需用到Newtonsoft.Json的高级特性。这部分是体现“深度”的核心,也是面试中区分“普通开发者”和“资深开发者”的关键。

1. 自定义JsonConverter(最核心的高级特性)

理解这一步时,当默认的序列化/反序列化逻辑无法满足需求时(如处理特殊类型:DateTimeOffset、Enum、自定义对象,或自定义JSON结构),能够借助继承JsonConverter抽象类,实现自定义的序列化/反序列化逻辑。

核心步骤:继承JsonConverter→重写CanConvert(判断是否兼容当前类型)→重写WriteJson(自定义序列化逻辑)→重写ReadJson(自定义反序列化逻辑)。

示例1:自定义Enum序列化(将Enum转为“值+描述”的JSON对象)

// 1. 定义一个带描述的Enum
public enum UserRole
{
    [Description("普通用户")]
    Normal = 1,
    [Description("管理员")]
    Admin = 2,
    [Description("超级管理员")]
    SuperAdmin = 3
}

// 2. 自定义Enum转换器
public class EnumDescriptionConverter : JsonConverter
{
    // 判断当前类型是否为Enum(支持所有Enum类型)
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsEnum;
    }

    // 自定义序列化逻辑:将Enum转为{ "Value": 1, "Description": "普通用户" }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Enum enumValue = (Enum)value;
        // 获取Enum的描述(需引用System.ComponentModel)
        string description = enumValue.GetDescription();
        // 写入JSON对象
        writer.WriteStartObject();
        writer.WritePropertyName("Value");
        writer.WriteValue((int)enumValue);
        writer.WritePropertyName("Description");
        writer.WriteValue(description);
        writer.WriteEndObject();
    }

    // 自定义反序列化逻辑:将JSON对象转为Enum
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // 读取JSON对象中的Value属性,转为Enum
        JObject jo = JObject.Load(reader);
        int value = jo["Value"].Value<int>();
        return Enum.Parse(objectType, value.ToString());
    }
}

// 3. 使用自定义转换器
var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    // 注册自定义转换器
    Converters = new List<JsonConverter> { new EnumDescriptionConverter() }
};

// 序列化Enum
UserRole role = UserRole.Admin;
string jsonEnum = JsonConvert.SerializeObject(role, settings);
Console.WriteLine("自定义Enum序列化结果:");
Console.WriteLine(jsonEnum);
// 输出:{"Value":2,"Description":"管理员"}

// 反序列化Enum
UserRole deserializedRole = JsonConvert.DeserializeObject<UserRole>(jsonEnum, settings);
Console.WriteLine("n自定义Enum反序列化结果:");
Console.WriteLine(deserializedRole); // 输出:Admin

示例2:自定义日期转换器(将日期转为时间戳)

// 自定义日期转换器:日期→毫秒时间戳
public class TimestampConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // 支持DateTime和DateTimeOffset类型
        return objectType == typeof(DateTime) || objectType == typeof(DateTimeOffset);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        DateTime dateTime;
        if (value is DateTimeOffset offset)
        {
            dateTime = offset.UtcDateTime;
        }
        else
        {
            dateTime = ((DateTime)value).ToUniversalTime();
        }
        // 计算毫秒时间戳(从1970-01-01 UTC开始)
        long timestamp = (long)(dateTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        writer.WriteValue(timestamp);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // 时间戳→日期
        long timestamp = (long)reader.Value;
        DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp);
        if (objectType == typeof(DateTimeOffset))
        {
            return new DateTimeOffset(dateTime);
        }
        return dateTime.ToLocalTime(); // 转为本地时间
    }
}

// 使用日期转换器
var settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new TimestampConverter() }
};

User user = new User { Id = 1, CreateTime = DateTime.Now };
string jsonTimestamp = JsonConvert.SerializeObject(user, settings);
Console.WriteLine("日期转为时间戳JSON:");
Console.WriteLine(jsonTimestamp);
// 输出:CreateTime的值为毫秒时间戳(如1716200000000)

2. 动态JSON处理(JObject、JArray、JValue)

实际处理时,真实开发场景下,有时JSON结构不固定(如接口得到的JSON字段动态变化),无法提前定义实体类,此时能够采用Newtonsoft.Json提供的动态JSON对象从实现思路看,(JObject、JArray、JValue),无需实体类,直接操作JSON节点。

核心类:

  • JObject:对应JSON对象({}),可借助键值对操作属性。
  • JArray:对应JSON数组([]),可借助索引操作元素。
  • JValue:对应JSON中的单个值(字符串、数字、布尔值等)。
// 1. 解析JSON字符串为JObject(动态操作)
string json = "{"Id":1,"Name":"张三","Age":25,"Hobbies":["篮球","游戏"]}";
JObject jObject = JObject.Parse(json);

// 读取属性(支持动态索引、类型转换)
int id = jObject["Id"].Value<int>();
string name = jObject["Name"].Value<string>();
List<string> hobbies = jObject["Hobbies"].ToObject<List<string>>();
Console.WriteLine($"Id: {id}, Name: {name}");

// 修改属性
jObject["Age"] = 26;
// 添加新属性
jObject["Address"] = "深圳";
// 删除属性
jObject.Remove("Hobbies");

// 转为JSON字符串
string modifiedJson = jObject.ToString(Formatting.Indented);
Console.WriteLine("n修改后的JSON:");
Console.WriteLine(modifiedJson);

// 2. 构建JArray(JSON数组)
JArray jArray = new JArray();
jArray.Add(JObject.FromObject(new User { Id = 1, Name = "张三" }));
jArray.Add(JObject.FromObject(new User { Id = 2, Name = "李四" }));
string arrayJson = jArray.ToString(Formatting.Indented);
Console.WriteLine("n构建的JSON数组:");
Console.WriteLine(arrayJson);

【应用场景】:接口得到JSON结构不固定、临时处理JSON数据、动态修改JSON节点等场景,比实体类更灵活。

3. 条件序列化(基于属性值或自定义逻辑)

落到代码里,有时需根据属性的具体值,决定是否将其序列化(比NullValueHandling更灵活),Newtonsoft.Json提供了两种方式:

方式1:JsonIgnoreAttribute的Condition属性

public class User
{
    public int Id { get; set; }
    // 当Name为null或空字符串时,忽略该属性
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNullOrEmpty)]
    public string Name { get; set; }
    // 当Age为默认值(0)时,忽略该属性
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public int Age { get; set; }
}

// 测试
User user = new User { Id = 1, Name = "", Age = 0 };
string jsonCondition = JsonConvert.SerializeObject(user, Formatting.Indented);
Console.WriteLine("条件序列化结果:");
Console.WriteLine(jsonCondition);
// 输出:只包含Id属性,Name和Age被忽略

方式2:实现IContractResolver自定义条件

实际处理时,适用来更复杂的条件(如根据属性值范围、业务逻辑判断),借助自定义ContractResolver,控制属性是否序列化。

// 自定义ContractResolver:年龄小于18的用户,忽略Age属性
public class CustomContractResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        // 判断是否为Age属性,且值小于18
        if (member.Name == "Age")
        {
            property.ShouldSerialize = instance =>
            {
                User user = (User)instance;
                return user.Age >= 18; // 年龄≥18才序列化Age属性
            };
        }
        return property;
    }
}

// 使用自定义ContractResolver
var settings = new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    ContractResolver = new CustomContractResolver()
};

User user1 = new User { Id = 1, Name = "张三", Age = 25 }; // Age≥18,序列化
User user2 = new User { Id = 2, Name = "李四", Age = 17 }; // Age<18,忽略Age
string json1 = JsonConvert.SerializeObject(user1, settings);
string json2 = JsonConvert.SerializeObject(user2, settings);
Console.WriteLine("用户1序列化结果:");
Console.WriteLine(json1); // 包含Age属性
Console.WriteLine("用户2序列化结果:");
Console.WriteLine(json2); // 不包含Age属性

4. 大数据量序列化/反序列化(性能优化)

落到代码里,处理大数据量(如10万条数据、大文件JSON)时,直接采用JsonConvert.SerializeObject/DeserializeObject会占用大量内存,甚至导致内存溢出,此时需采用流操作落到代码里,(JsonTextWriter、JsonTextReader),实现逐行、按需处理,降低内存占用。

示例:大数据量序列化(采用JsonTextWriter)

// 模拟大数据量(10万条用户数据)
List<User> largeList = new List<User>();
for (int i = 0; i < 100000; i++)
{
    largeList.Add(new User { Id = i + 1, Name = $"用户{i + 1}", Age = 20 + (i % 30) });
}

// 使用JsonTextWriter,逐行写入流,降低内存占用
using (FileStream fs = new FileStream("largeData.json", FileMode.Create))
using (StreamWriter sw = new StreamWriter(fs))
using (JsonTextWriter writer = new JsonTextWriter(sw))
{
    writer.Formatting = Formatting.Indented;
    // 开始写入JSON数组
    writer.WriteStartArray();
    foreach (var user in largeList)
    {
        // 逐行写入用户对象
        JsonSerializer.CreateDefault().Serialize(writer, user);
    }
    // 结束JSON数组
    writer.WriteEndArray();
}
Console.WriteLine("大数据量序列化完成,文件已保存");

示例:大数据量反序列化(采用JsonTextReader)

// 使用JsonTextReader,逐行读取,按需处理,不加载全部数据到内存
using (FileStream fs = new FileStream("largeData.json", FileMode.Open))
using (StreamReader sr = new StreamReader(fs))
using (JsonTextReader reader = new JsonTextReader(sr))
{
    JsonSerializer serializer = JsonSerializer.CreateDefault();
    // 读取JSON数组
    reader.Read(); // 读取数组开始标记 [
    while (reader.Read())
    {
        // 逐行读取用户对象,处理后释放内存
        if (reader.TokenType == JsonToken.StartObject)
        {
            User user = serializer.Deserialize<User>(reader);
            // 处理用户数据(如写入数据库)
            Console.WriteLine($"处理用户:{user.Id} - {user.Name}");
        }
    }
}
Console.WriteLine("大数据量反序列化完成");

【性能优化要点】:

  • 理解这一步时,大数据量场景,优先采用流操作(JsonTextWriter/JsonTextReader),避免一次性加载全部数据到内存。
  • 结合项目来看,关闭不必要的设置(如Formatting.Indented,美化格式会增加性能开销)。
  • 结合项目来看,采用JsonSerializer.CreateDefault()新建实例,避免重复新建,提升性能。

五、底层原理:Newtonsoft.Json的序列化机制(深度深挖)

从实现思路看,面试中,除了考察用法,有时还会追问“Newtonsoft.Json的序列化原理是什么”,这部分我们从底层逻辑入手,轻松易懂地拆解其核心流程,无需深入源码,掌握核心逻辑即可。

1. 序列化核心流程(.NET对象→JSON字符串)

  • 类型解析:接收.NET对象后,借助反射(Reflection)拿到对象的类型信息(类、属性、字段、方法),确定需序列化的成员(默认序列化公共属性,可借助设置修改)。
  • 契约构建:根据对象类型和设置(JsonSerializerSettings、特性),构建“序列化契约”(Contract),明确每个成员的序列化规则(如是否忽略、命名策略、转换器等)。
  • 值转换:对每个成员的值进行转换,将.NET类型转为JSON兼容的类型(如DateTime→字符串/时间戳、Enum→字符串/数字、自定义类型→JSON对象),过程中会调用自定义转换器(若有)。
  • JSON构建:借助JsonWriter(或其派生类),将转换后的值按JSON格式(对象、数组、键值对)写入流或字符串,最后生成JSON字符串。

2. 反序列化核心流程(JSON字符串→.NET对象)

  • JSON解析:将JSON字符串解析为抽象语法树(AST),借助JsonReader读取JSON节点(对象、数组、值),识别JSON的结构和类型。
  • 类型匹配:根据目标.NET类型,将JSON节点与对象的属性/字段进行匹配(按名称匹配,可借助设置修改匹配规则)。
  • 值转换:将JSON节点的值转为.NET类型(如字符串→DateTime、数字→int、JSON对象→自定义类),过程中会调用自定义转换器(若有)。
  • 对象构建:借助反射新建目标类型的实例,将转换后的值赋值给对应的属性/字段,最后得到构建好的.NET对象。

3. 核心设计模式

落到代码里,Newtonsoft.Json的底层采用了多种设计模式,保证其灵活性和可扩展性:

  • 策略模式:借助JsonSerializerSettings、ContractResolver,定义不同的序列化策略,灵活切换。
  • 转换器模式:借助JsonConverter抽象类,将不同类型的序列化/反序列化逻辑封装,实现扩展。
  • 工厂模式:借助JsonSerializer.Create()新建序列化器实例,根据设置生成不同的序列化器。

六、实际开发避坑指南(高频问题+解决方案)

在这个场景下,这部分是最实用的内容,整理了真实开发场景下采用Newtonsoft.Json时最常遇到的问题、报错及解决方案,帮你避免踩坑,提高开发效率。

坑1:引用循环导致序列化报错(InvalidOperationException)

报错信息:Self referencing loop detected with type 'XXX'. Path 'XXX.XXX'.

解决方案:设置ReferenceLoopHandling.Ignore或ReferenceLoopHandling.Serialize,具体见“核心设置”部分。

坑2:日期序列化格式不一致,反序列化失败

问题:JSON中的日期格式(如"2024-05-20 15:30:00")与默认的ISO格式不一致,反序列化时无法转为DateTime。

解决方案:设置DateFormatString,指定与JSON一致的日期格式;或采用自定义日期转换器。

坑3:JSON属性名与.NET实体类属性名不匹配,反序列化后属性为默认值

问题:JSON属性名是驼峰(userName),实体类是帕斯卡(UserName),或属性名完全不同(如JSON是user_name,实体类是UserName)。

解决方案:

  • 方式1:采用CamelCasePropertyNamesContractResolver,自动转换命名。
  • 方式2:给实体类属性添加JsonProperty特性,指定JSON属性名(如[JsonProperty(“user_name”)])。

坑4:大数据量序列化/反序列化导致内存溢出

问题:一次性序列化/反序列化大量数据,内存占用过高,导致OutOfMemoryException。

解决方案:采用流操作(JsonTextWriter/JsonTextReader),逐行处理,避免一次性加载全部数据到内存,具体见“高级特性”部分。

坑5:自定义转换器不生效

问题:继承JsonConverter后,序列化/反序列化时未按自定义逻辑执行。

解决方案:

  • 结合项目来看,确保CanConvert方法正确判断目标类型(如得到objectType == typeof(Enum))。
  • 理解这一步时,在JsonSerializerSettings的Converters中注册自定义转换器。
  • 避免多个转换器同时兼容同一类型,导致冲突。

坑6:序列化时敏感信息(如密码)被泄露

问题:实体类中的敏感属性(如Password、Token)被序列化到JSON中,对外暴露。

解决方案:给敏感属性添加[JsonIgnore]特性,强制忽略序列化;或采用条件序列化,控制敏感属性不被序列化。

七、总结:Newtonsoft.Json的核心价值与适用场景

理解这一步时,Newtonsoft.Json作为.NET生态中最成熟的JSON处理类库,其核心价值在于“功能全面、灵活易用、兼容性强实际处理时,”,无论是轻松的对象序列化,还是复杂的自定义JSON处理、大数据量场景,都能轻松应对。

1. 核心优势总结

  • 兼容性强:兼容所有.NET平台,适配老项目和新项目。
  • 设置灵活:借助JsonSerializerSettings和特性,精细控制序列化/反序列化逻辑。
  • 扩展能力强:兼容自定义转换器、动态JSON处理,满足复杂场景需求。
  • 生态成熟:社区资源丰富,bug修复及时,第三方框架默认集成。

2. 适用场景

  • 接口开发:与前端、第三方接口进行JSON数据交互(最常用场景)。
  • 设置解析:读取JSON格式的设置文件(如appsettings.json)。
  • 数据存储:将.NET对象序列化为JSON字符串,存储到数据库、文件中。
  • 复杂JSON处理:动态JSON、自定义JSON结构、特殊类型处理(如Enum、DateTime)。
  • 老项目维护:.NET Framework项目,无法采用System.Text.Json的场景。

3. 最后提醒

理解这一步时,虽然Newtonsoft.Json功能强大,但在.NET 5+新项目中,若场景轻松、追求极致性能,可考虑采用微软官方的System.Text.Json;若需复杂设置、兼容性或依赖第三方生态,Newtonsoft.Json依然是首选。无论选择哪种,掌握JSON处理的核心逻辑(序列化/反序列化流程、设置控制、自定义扩展),才是关键。

从实现思路看,总的来说,C# Newtonsoft.Json适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。

相关资讯

最新游戏

更多

Copyright©2010-2019. All rights reserved | 波波三国游戏官网|[email protected]

备案编号:湘ICP备2022015115号-4