当前位置:

首页 > 编程开发 > C# Json.NET解析JSON方法详解

C# Json.NET解析JSON方法详解

强类型反序列化适用于结构固定的JSON,通过定义C#类直接映射,提升类型安全和代码可读性;2.动态解析使用JObject/JArray,适合结构不确定或只需访问部分字段的场景,灵活性高;3.嵌套结构在强类型中通过类嵌套自动映射,在动态解析中通过链式索引访问;4.数组在强类型中映射为List<T>或T[],动态解析中转为JArray遍历处理;5.缺失字段可用可空类型、默认值或JsonProperty.Required控制,动态访问需null检查;6.类型不匹配会抛异常,可用try-catch捕获

强类型反序列化适用于结构固定的JSON,通过定义C#类直接映射,提升类型安全和代码可读性;2. 动态解析使用JObject/JArray,适合结构不确定或只需访问部分字段的场景,灵活性高;3. 嵌套结构在强类型中通过类嵌套自动映射,在动态解析中通过链式索引访问;4. 数组在强类型中映射为List或T[],动态解析中转为JArray遍历处理;5. 缺失字段可用可空类型、默认值或JsonProperty.Required控制,动态访问需null检查;6. 类型不匹配会抛异常,可用try-catch捕获,或自定义JsonConverter实现复杂转换;7. 性能优化包括避免中间对象、流式解析大文件、重用JsonSerializerSettings;8. 高级用法涵盖自定义JsonConverter、条件序列化ShouldSerialize、JsonProperty更多配置及循环引用处理,完整实现灵活高效的数据解析。

C#的Json.NET库如何解析JSON数据?

C#中使用Json.NET库解析JSON数据,核心在于将JSON字符串映射到C#对象。你可以选择强类型反序列化到预定义的类,或者使用JObject/JArray进行动态解析,这取决于你对JSON结构了解的程度和需求。

解决方案

谈到Json.NET解析JSON,我个人觉得它最迷人的地方在于其兼顾了便捷性和灵活性。很多时候,我们面对的JSON结构并非一成不变,或者我们只想抽取其中某个小片段,这时Json.NET的两种主要思路就派上用场了。

1. 强类型反序列化:当你对JSON结构了如指掌时

这是最推荐也最简洁的方式。如果你的JSON结构是固定的,或者你可以预先定义好对应的C#类,那么直接将JSON字符串反序列化到这些类实例上,能带来极佳的代码可读性和类型安全性。

比如,我们有这样一个JSON:

{
  "productId": 101,
  "productName": "Laptop Pro",
  "price": 1200.50,
  "tags": ["electronics", "computers", "portable"],
  "details": {
    "weightKg": 1.8,
    "color": "Space Gray"
  }
}

你可以这样定义C#类:

public class Product
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public decimal Price { get; set; }
    public List Tags { get; set; }
    public ProductDetails Details { get; set; }
}

public class ProductDetails
{
    public double WeightKg { get; set; }
    public string Color { get; set; }
}

然后,解析就变得异常简单:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

// 假设jsonString是上述JSON内容
string jsonString = @"{
  ""productId"": 101,
  ""productName"": ""Laptop Pro"",
  ""price"": 1200.50,
  ""tags"": [""electronics"", ""computers"", ""portable""],
  ""details"": {
    ""weightKg"": 1.8,
    ""color"": ""Space Gray""
  }
}";

try
{
    Product product = JsonConvert.DeserializeObject(jsonString);

    Console.WriteLine($"产品ID: {product.ProductId}");
    Console.WriteLine($"产品名称: {product.ProductName}");
    Console.WriteLine($"价格: {product.Price}");
    Console.WriteLine($"第一个标签: {product.Tags?[0]}"); // 注意null条件运算符
    Console.WriteLine($"颜色: {product.Details?.Color}");
}
catch (JsonSerializationException ex)
{
    Console.WriteLine($"反序列化错误: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"发生未知错误: {ex.Message}");
}

2. 动态解析:当你对JSON结构不确定或只想快速访问部分数据时

有时候,我们可能不清楚JSON的具体结构,或者只想获取其中一两个字段的值,为它专门定义一个类显得有些小题大做。这时,JObject和JArray就显得非常灵活了。它们允许你像操作字典或数组一样,通过键名或索引来访问JSON中的数据。

继续以上面的JSON为例:

using Newtonsoft.Json.Linq; // JObject, JArray都在这个命名空间下

string jsonString = @"{
  ""productId"": 101,
  ""productName"": ""Laptop Pro"",
  ""price"": 1200.50,
  ""tags"": [""electronics"", ""computers"", ""portable""],
  ""details"": {
    ""weightKg"": 1.8,
    ""color"": ""Space Gray""
  }
}";

try
{
    JObject jsonObject = JObject.Parse(jsonString);

    // 访问基本类型
    int productId = (int)jsonObject["productId"];
    string productName = (string)jsonObject["productName"];
    decimal price = jsonObject["price"].Value(); // 另一种获取值的方式

    Console.WriteLine($"产品ID (动态): {productId}");
    Console.WriteLine($"产品名称 (动态): {productName}");

    // 访问嵌套对象
    JObject details = (JObject)jsonObject["details"];
    string color = (string)details["color"];
    Console.WriteLine($"颜色 (动态): {color}");

    // 访问数组
    JArray tags = (JArray)jsonObject["tags"];
    foreach (JToken tag in tags)
    {
        Console.WriteLine($"标签: {tag.ToString()}");
    }

    // 安全地获取可能不存在的属性
    string nonExistentProperty = (string)jsonObject["nonExistent"]?.Value();
    Console.WriteLine($"不存在的属性: {nonExistentProperty ?? "未找到"}");
}
catch (JsonReaderException ex)
{
    Console.WriteLine($"JSON解析错误: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"发生未知错误: {ex.Message}");
}

这种动态解析方式在处理API响应不一致、或者需要快速原型验证时非常有用。它允许你先拿到数据,再决定如何处理,而不用急着定义一堆类。

如何处理JSON中的嵌套结构和数组?

处理JSON中的嵌套结构和数组,其实是Json.NET的强项,无论是强类型还是动态解析,都有非常直观的方法。我发现很多初学者在这里会有点困惑,觉得嵌套就复杂了,但实际上,只要理解了映射关系,它比你想象的要简单。

对于嵌套对象:

在强类型反序列化中,只需要在你的C#父类中,将子对象定义为另一个C#类的实例即可。Json.NET会自动递归地进行映射。比如,如果你的JSON有一个"user": { "name": "Alice", "age": 30 },那么你的RootObject类中就应该有一个public User User { get; set; }属性,而User类则包含Name和Age属性。

// JSON: { "orderId": "123", "customer": { "name": "Bob", "email": "bob@example.com" } }
public class Order
{
    public string OrderId { get; set; }
    public Customer Customer { get; set; } // 嵌套对象
}

public class Customer
{
    public string Name { get; set; }
    public string Email { get; set; }
}

// 解析
Order myOrder = JsonConvert.DeserializeObject(jsonString);
Console.WriteLine($"客户姓名: {myOrder.Customer.Name}");

在使用JObject动态解析时,嵌套对象就像是多层字典。你可以通过链式索引来访问:

JObject orderJson = JObject.Parse(jsonString);
string customerName = (string)orderJson["customer"]["name"]; // 链式访问
Console.WriteLine($"客户姓名 (动态): {customerName}");

对于数组:

数组的处理同样直接。强类型反序列化时,JSON数组通常映射到C#的List或T[]。如果数组包含复杂对象,那么T就是你定义的那个复杂对象类。

// JSON: { "productName": "Keyboard", "features": ["mechanical", "rgb", "wireless"] }
public class ProductWithFeatures
{
    public string ProductName { get; set; }
    public List Features { get; set; } // 字符串数组
}

// JSON: { "teamName": "Devs", "members": [{ "name": "Alice" }, { "name": "Bob" }] }
public class Team
{
    public string TeamName { get; set; }
    public List Members { get; set; } // 对象数组
}

public class Member
{
    public string Name { get; set; }
}

// 解析
ProductWithFeatures keyboard = JsonConvert.DeserializeObject(jsonString1);
Console.WriteLine($"第一个特性: {keyboard.Features[0]}");

Team devTeam = JsonConvert.DeserializeObject(jsonString2);
Console.WriteLine($"第一个成员: {devTeam.Members[0].Name}");

动态解析时,JSON数组会被解析成JArray。你可以像遍历C#数组一样遍历它,然后对每个JToken元素进行类型转换或进一步解析:

JObject productFeaturesJson = JObject.Parse(jsonString1);
JArray featuresArray = (JArray)productFeaturesJson["features"];
foreach (JToken feature in featuresArray)
{
    Console.WriteLine($"特性: {feature.ToString()}");
}

JObject teamJson = JObject.Parse(jsonString2);
JArray membersArray = (JArray)teamJson["members"];
foreach (JObject member in membersArray) // 注意这里直接转为JObject
{
    Console.WriteLine($"成员姓名: {(string)member["name"]}");
}

关键在于,Json.NET的设计理念就是将JSON的层级结构自然地映射到C#的对象图,无论是显式定义类还是通过JObject/JArray的嵌套访问,都保持了高度的一致性。

解析时遇到数据类型不匹配或缺失字段怎么办?

在实际开发中,JSON数据源往往不像示例那样完美,可能会出现字段缺失、数据类型不匹配,甚至额外字段的情况。处理这些“不完美”是健壮代码的体现,Json.NET提供了不少策略来应对。

1. 缺失字段:

  • 默认值和可空类型: 如果一个字段可能缺失,最简单的方法是将其C#属性定义为可空类型(如int?,decimal?)或者赋一个默认值。

    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public decimal? Price { get; set; } // Price可能缺失,所以用decimal?
        public string Description { get; set; } = "无描述"; // 缺失时使用默认值
    }

    当JSON中没有Price字段时,Price属性会被设为null。没有Description时,会保持“无描述”。

  • JsonProperty属性的Required选项: 如果某个字段是强制性的,缺失就应该报错,你可以使用[JsonProperty(Required = Required.Always)]。

    public class User
    {
        [JsonProperty(Required = Required.Always)] // 强制要求此字段必须存在
        public string Username { get; set; }
        public int Age { get; set; }
    }
    // 如果JSON中没有Username,会抛出JsonSerializationException

    Required.Default是默认值,表示如果JSON中没有该字段,则使用C#属性的默认值(null或0等)。Required.AllowNull表示字段可以缺失,如果存在但为null,则也允许。

  • 动态解析时的空值检查: 使用JObject时,在访问属性前进行null检查,或者使用C# 8的空条件运算符?.。

    JObject data = JObject.Parse("{ \"name\": \"Alice\" }");
    string email = (string)data["email"]?.Value(); // 如果email不存在,email会是null
    Console.WriteLine($"Email: {email ?? "N/A"}");

2. 数据类型不匹配:

这是最常见的错误源之一。比如JSON里一个数字字段,但C#里定义成了字符串。

  • JsonConvert.DeserializeObject抛出异常: 默认情况下,如果类型不匹配,Json.NET会抛出JsonSerializationException。这是好事,因为它提醒你数据结构与模型不符。

    // JSON: { "age": "thirty" } 但C#类中 age 是 int
    public class Person { public int Age { get; set; } }
    // JsonConvert.DeserializeObject(json) 会抛出异常

    处理方式通常是在外层用try-catch捕获这个异常,然后根据业务需求进行错误记录、重试或返回错误信息。

  • 自定义JsonConverter: 对于复杂的类型转换,例如JSON中的日期字符串格式不固定,或者需要将某个特定字符串映射到枚举类型,你可以实现一个自定义的JsonConverter。这提供了非常强大的控制力,让你能精确定义如何从JSON读取数据以及如何写入JSON。

    // 假设JSON中的日期是 "2023/10/27" 而不是标准ISO 8601
    public class CustomDateTimeConverter : JsonConverter
    {
        public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.String)
            {
                return DateTime.ParseExact((string)reader.Value, "yyyy/MM/dd", null);
            }
            return default; // 或者抛出异常
        }
        // ... WriteJson 方法
    }
    
    public class Event
    {
        [JsonConverter(typeof(CustomDateTimeConverter))]
        public DateTime EventDate { get; set; }
    }

    然后JsonConvert.DeserializeObject(json)就会使用你的自定义转换器。

  • JToken.ToObject(): 在动态解析时,如果你不确定某个JToken的实际类型,但想尝试将其转换为特定C#类型,可以使用JToken.ToObject()。它会尝试进行转换,如果失败会抛出异常。

    JObject data = JObject.Parse("{ \"count\": \"123\" }");
    try
    {
        int count = data["count"].ToObject(); // 尝试将字符串"123"转为int
        Console.WriteLine($"Count: {count}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"转换失败: {ex.Message}");
    }

总的来说,处理这些异常情况,核心思想是:预判可能的问题,利用Json.NET提供的特性(如可空类型、Required属性、自定义转换器),并在最外层进行try-catch,确保程序的健壮性。

性能优化与高级用法有哪些考量?

Json.NET作为一个广泛使用的库,在性能和高级功能方面都有不少值得探讨的地方。日常使用可能很少触及,但当处理大量数据、追求极致性能,或者有特殊序列化需求时,这些考量就显得很重要了。

1. 性能优化:

  • 避免不必要的中间对象: 如果你只需要JSON中的一小部分数据,并且这些数据是扁平的,直接使用JObject或JArray的索引访问通常比先反序列化成一个庞大的强类型对象再取值更高效,因为省去了创建大量C#对象的开销。

  • 流式解析(JsonTextReader): 对于非常大的JSON文件(比如几百MB甚至GB级别),一次性将整个JSON字符串加载到内存并解析成JObject或强类型对象可能会导致内存溢出。这时,使用JsonTextReader进行流式读取是最佳实践。它允许你逐个读取JSON令牌(如开始对象、属性名、值、结束对象等),从而在不将整个JSON加载到内存的情况下处理数据。这就像读文件一样,一行一行地读,而不是一次性读完。

    using (JsonTextReader reader = new JsonTextReader(new StringReader(largeJsonString)))
    {
        while (reader.Read())
        {
            if (reader.TokenType == JsonToken.PropertyName && reader.Value.ToString() == "targetProperty")
            {
                reader.Read(); // 读取属性值
                Console.WriteLine($"找到目标属性值: {reader.Value}");
                break;
            }
        }
    }

    当然,流式解析的缺点是需要手动管理状态,代码会复杂一些。

  • 重用JsonSerializerSettings: 如果你在应用程序中多次进行序列化/反序列化,并且使用了自定义设置(如日期格式、命名策略等),最好将JsonSerializerSettings实例创建一次并重用,而不是每次都新建。这可以减少GC压力和初始化开销。

  • 选择合适的序列化/反序列化方法: JsonConvert.SerializeObject和JsonConvert.DeserializeObject是静态方法,内部会创建JsonSerializer实例。如果需要频繁操作且有特定设置,直接创建和重用JsonSerializer实例会更高效。

    JsonSerializer serializer = new JsonSerializer();
    serializer.Converters.Add(new CustomDateTimeConverter());
    // ... 其他设置
    
    using (StringReader sr = new StringReader(jsonString))
    using (JsonTextReader reader = new JsonTextReader(sr))
    {
        MyObject obj = serializer.Deserialize(reader);
    }

2. 高级用法:

  • 自定义序列化/反序列化逻辑(JsonConverter): 前面提到了处理类型不匹配,但JsonConverter的用途远不止于此。你可以用它来处理多态(根据JSON中的某个字段决定反序列化成哪个子类)、特殊数据结构(如将一个对象序列化成数组形式)、或者对特定属性进行加密/解密。这需要继承JsonConverter并实现ReadJson和WriteJson方法。

  • 条件序列化(ShouldSerialize): 有时候你可能不希望所有属性都被序列化到JSON中,例如某些属性只有在满足特定条件时才需要。Json.NET允许你在类中定义一个名为ShouldSerialize[PropertyName]的方法,返回bool,来控制对应属性是否被序列化。

    public class UserProfile
    {
        public string Username { get; set; }
        public string Email { get; set; }
        public bool IsAdmin { get; set; }
    
        // 只有当IsAdmin为true时才序列化Email
        public bool ShouldSerializeEmail()
        {
            return IsAdmin;
        }
    }
  • JsonProperty属性的更多用法: 除了Required,JsonProperty还有很多其他有用的特性,比如PropertyName用于C#属性名和JSON字段名不一致的情况,Order用于控制JSON输出中属性的顺序,DefaultValue用于当属性值为默认值时是否序列化等。

  • 处理循环引用(ReferenceLoopHandling): 当你的C#对象图存在循环引用(A引用B,B又引用A)时,默认序列化会进入无限循环并抛出异常。你可以通过ReferenceLoopHandling设置来控制行为,例如Ignore(忽略循环引用)、Serialize(序列化所有引用,可能导致无限循环)、Error(默认,抛出异常)。通常设置为ReferenceLoopHandling.Ignore或ReferenceLoopHandling.Serialize配合PreserveReferencesHandling来解决。

  • 错误处理回调(Error事件): 在序列化或反序列化过程中,你可以通过JsonSerializerSettings.Error事件注册回调,来捕获和处理发生在特定属性上的错误,而不是直接抛出整个操作的异常。这在处理部分有效但部分损坏的JSON时非常有用。

这些高级用法,就像是Json

本文内容来源于网友投稿,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
CSS设置透明度的注意事项有哪些?opacity属性详解
CSS设置透明度的注意事项有哪些?opacity属性详解

深入解析CSS中设置透明度的核心属性opacity,剖析子元素继承、事件穿透、层叠上下文等关键注意事项,并提供与rgba、hsla的实用选型对比。

flutter页面传值到后台的方法及示例代码
flutter页面传值到后台的方法及示例代码

flutter页面传值到后台的完整实现方法及示例代码,帮助读者快速掌握相关技术要点。

Java 8至21新特性代码写法对比:Lambda、Record与Switch
Java 8至21新特性代码写法对比:Lambda、Record与Switch

本文通过具体的旧版与新版代码对比,详细剖析Java 8引入的Lambda表达式、Java 14/16引入的Record类,以及Java 12至21逐步演进完善的Switch表达式与模式匹配,展示代码简化路径与避坑要点。

AI智能体开发培训课程学什么及实战内容介绍
AI智能体开发培训课程学什么及实战内容介绍

系统梳理AI智能体开发培训的核心知识模块、技术栈选型与典型实战项目,解析低代码平台与纯代码框架的差异,提供从零构建可落地智能体的完整学习与实施路径。

Java子类未实现抽象方法编译错误修复指南
Java子类未实现抽象方法编译错误修复指南

针对Java开发中常见的“子类未实现抽象方法”编译错误,深入分析报错原因,提供重写实现、声明抽象子类两种标准修复路径,并总结参数签名、访问修饰符等典型避坑要点。

解决PHP递归报错:max_nesting_level限制与内存溢出处理
解决PHP递归报错:max_nesting_level限制与内存溢出处理

遇到PHP递归报错时,不要盲目调大max_nesting_level。本文教你区分Xdebug限制、内存耗尽和正则递归错误,提供代码级的终止条件优化与迭代替代方案,彻底解决栈溢出问题。

PHP递归中static变量与引用传递的常见陷阱及调试
PHP递归中static变量与引用传递的常见陷阱及调试

本文分析PHP递归中static变量导致的状态污染及引用传递引发的共享数据修改问题。提供具体的代码复现、缓存键设计建议及调试打印技巧,帮助开发者避免隐蔽的逻辑错误。

PHP递归性能优化技巧与迭代替代方案
PHP递归性能优化技巧与迭代替代方案

解析PHP递归函数在树形数据处理中的性能瓶颈,提供预加载数据消除I/O、使用显式栈替代深层递归的实战方案,帮助开发者在代码可读性与执行效率间做出合理取舍。

Java测试中怎么使用Mockito模拟依赖对象
Java测试中怎么使用Mockito模拟依赖对象

详细讲解在Java单元测试中如何使用Mockito模拟依赖对象,包括引入依赖、创建Mock、打桩返回值、行为验证以及Mock与Spy的核心差异和常见陷阱排查。

链表删除节点的时间复杂度是多少及其详细分析
链表删除节点的时间复杂度是多少及其详细分析

详细分析链表删除节点的时间复杂度,深入探讨单链表与双向链表在不同已知前提下的查找与删除开销,并结合完整代码与清晰图解进行对比总结。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

WINDOWS 更多
3dmax(3ds max)
3dmax(3ds max)
Windows

Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。