1. JsonConvert基础入门:从零开始处理JSON数据
第一次接触JSON数据处理时,我完全被各种花括号和方括号搞晕了。后来发现C#中的JsonConvert简直就是处理JSON的神器,它属于Newtonsoft.Json库(现在也叫Json.NET),是目前.NET生态中最流行的JSON处理工具。
安装它只需要在NuGet包管理器中搜索"Newtonsoft.Json",或者通过命令行:
Install-Package Newtonsoft.Json基础用法简单到令人发指。比如有个用户数据:
{ "name": "张三", "age": 25, "isVIP": true }用三行代码就能转换成对象:
using Newtonsoft.Json; string json = "{\"name\":\"张三\",\"age\":25,\"isVIP\":true}"; User user = JsonConvert.DeserializeObject<User>(json); Console.WriteLine($"用户名:{user.Name}");这里的User类长这样:
public class User { public string Name { get; set; } public int Age { get; set; } public bool IsVIP { get; set; } }反过来把对象转JSON更简单:
User newUser = new User { Name = "李四", Age = 30, IsVIP = false }; string newJson = JsonConvert.SerializeObject(newUser); Console.WriteLine(newJson); // 输出:{"Name":"李四","Age":30,"IsVIP":false}2. 实战解析复杂JSON结构
2.1 处理嵌套对象
实际项目中遇到的JSON很少像上面那么简单。比如电商平台的订单数据:
{ "orderId": "20230615001", "customer": { "name": "王五", "phone": "13800138000" }, "items": [ { "productId": "P1001", "quantity": 2 }, { "productId": "P1005", "quantity": 1 } ] }对应的C#类结构:
public class Order { public string OrderId { get; set; } public Customer Customer { get; set; } public List<OrderItem> Items { get; set; } } public class Customer { public string Name { get; set; } public string Phone { get; set; } } public class OrderItem { public string ProductId { get; set; } public int Quantity { get; set; } }解析代码依然简洁:
Order order = JsonConvert.DeserializeObject<Order>(jsonString); Console.WriteLine($"订单包含{order.Items.Count}件商品");2.2 动态解析与JObject
有时候我们不想定义完整的类结构,或者JSON结构不固定,可以用JObject动态解析:
JObject jObj = JObject.Parse(jsonString); string customerName = (string)jObj["customer"]["name"]; JArray items = (JArray)jObj["items"]; int totalQuantity = items.Sum(item => (int)item["quantity"]);动态解析特别适合处理第三方API返回的不规则数据。比如有个天气API返回:
{ "status": "ok", "data": { "temperature": 26, "humidity": 0.78, "extra": { "wind_speed": 3.2, "visibility": "good" } } }我们可以只提取需要的字段:
var weather = JObject.Parse(weatherJson); if ((string)weather["status"] == "ok") { double temp = (double)weather["data"]["temperature"]; string visibility = (string)weather["data"]["extra"]["visibility"]; }3. 高级技巧与性能优化
3.1 自定义序列化设置
JsonConvert提供了丰富的配置选项。比如格式化输出、处理空值、日期格式等:
JsonSerializerSettings settings = new JsonSerializerSettings { Formatting = Formatting.Indented, // 美化输出 NullValueHandling = NullValueHandling.Ignore, // 忽略null值 DateFormatString = "yyyy-MM-dd HH:mm:ss" // 日期格式 }; string prettyJson = JsonConvert.SerializeObject(order, settings);3.2 处理特殊类型
遇到DateTime、Guid等特殊类型时,默认序列化可能不符合需求。我们可以自定义转换器:
public class CustomDateTimeConverter : JsonConverter<DateTime> { public override DateTime ReadJson(JsonReader reader, Type type, DateTime existing, bool hasValue, JsonSerializer serializer) { return DateTime.ParseExact(reader.Value.ToString(), "yyyy-MM-dd", CultureInfo.InvariantCulture); } public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer) { writer.WriteValue(value.ToString("yyyy-MM-dd")); } } // 使用自定义转换器 JsonConvert.SerializeObject(order, new CustomDateTimeConverter());3.3 性能优化建议
处理大量JSON数据时,这几个技巧可以提升性能:
- 重用JsonSerializerSettings实例
- 对于只读操作,使用JsonTextReader直接流式读取
- 使用内存池减少GC压力
// 流式读取大文件 using (StreamReader file = File.OpenText("large.json")) using (JsonTextReader reader = new JsonTextReader(file)) { while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { JObject obj = JObject.Load(reader); // 处理单个对象 } } }4. 实战案例:电商平台订单处理系统
假设我们要开发一个订单处理系统,需要处理这样的订单数据:
{ "orderId": "ORDER_12345", "createTime": "2023-06-15T14:30:00", "status": "paid", "products": [ { "sku": "SKU1001", "name": "无线鼠标", "price": 129.9, "specs": { "color": "black", "weight": 0.2 } }, { "sku": "SKU2003", "name": "机械键盘", "price": 399.0, "specs": { "color": "silver", "weight": 1.2, "switchType": "red" } } ], "payment": { "method": "alipay", "amount": 528.9, "transactionId": "ALI123456789" }, "delivery": { "address": "北京市海淀区", "receiver": "张三", "phone": "13800138000" } }首先定义对应的类结构:
public class Order { public string OrderId { get; set; } public DateTime CreateTime { get; set; } public string Status { get; set; } public List<Product> Products { get; set; } public PaymentInfo Payment { get; set; } public DeliveryInfo Delivery { get; set; } } public class Product { public string Sku { get; set; } public string Name { get; set; } public decimal Price { get; set; } public Dictionary<string, object> Specs { get; set; } } public class PaymentInfo { public string Method { get; set; } public decimal Amount { get; set; } public string TransactionId { get; set; } } public class DeliveryInfo { public string Address { get; set; } public string Receiver { get; set; } public string Phone { get; set; } }处理逻辑示例:
// 解析订单 Order order = JsonConvert.DeserializeObject<Order>(orderJson); // 计算订单总价 decimal total = order.Products.Sum(p => p.Price); bool priceValid = total == order.Payment.Amount; // 检查特殊商品 bool hasHeavyProduct = order.Products.Any(p => p.Specs.ContainsKey("weight") && Convert.ToDecimal(p.Specs["weight"]) > 1.0); // 生成简化的物流信息 var shippingInfo = new { OrderId = order.OrderId, Receiver = order.Delivery.Receiver, Phone = order.Delivery.Phone.Substring(0, 3) + "****" + order.Delivery.Phone.Substring(7), ProductCount = order.Products.Count }; string shippingJson = JsonConvert.SerializeObject(shippingInfo);5. 常见问题排查与调试技巧
5.1 类型不匹配问题
最常见的错误是JSON中的类型与C#属性类型不匹配。比如JSON中是字符串"123",但C#属性是int。解决方法:
// 错误示例 public class WrongExample { public int Age { get; set; } // JSON中是"age":"25"会报错 } // 正确做法:使用JsonProperty指定名称,并确保类型兼容 public class CorrectExample { [JsonProperty("age")] public int Age { get; set; } }5.2 处理多态类型
当JSON中的对象可能是多种类型时,可以用TypeNameHandling:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }; string json = JsonConvert.SerializeObject(obj, settings); var result = JsonConvert.DeserializeObject<BaseType>(json, settings);5.3 调试技巧
当JSON解析出错时,可以这样排查:
- 先用JToken.Parse验证JSON格式是否正确
- 使用JsonConvert.PopulateObject逐步填充对象
- 实现错误处理:
try { var obj = JsonConvert.DeserializeObject<MyType>(json); } catch (JsonException ex) { Console.WriteLine($"解析失败:{ex.Message}"); Console.WriteLine($"错误位置:{ex.Path}"); Console.WriteLine($"原始JSON片段:{json.Substring(ex.LinePosition - 20, 40)}"); }6. 最佳实践与性能对比
6.1 JsonConvert vs System.Text.Json
.NET Core 3.0引入了System.Text.Json,性能更好但功能较少。对比选择:
| 特性 | JsonConvert | System.Text.Json |
|---|---|---|
| 性能 | 中等 | 高 |
| 功能 | 丰富 | 基础 |
| 自定义 | 灵活 | 有限 |
| 依赖 | 第三方 | 官方内置 |
迁移建议:新项目可以考虑System.Text.Json,现有项目继续用JsonConvert更稳妥。
6.2 内存优化技巧
处理大JSON文件时:
- 使用StreamReader避免全量读取
- 选择合适的数据结构(如Dictionary代替JObject)
- 禁用不必要的特性(如注释、元数据)
var settings = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore };6.3 安全注意事项
- 始终验证外部JSON数据
- 限制最大解析深度(MaxDepth)
- 处理循环引用:
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;7. 真实项目经验分享
在最近的一个物联网项目中,我们需要处理设备上报的JSON数据。设备数据格式复杂且经常变化,我们最终采用了这样的方案:
- 基础信息用强类型类解析
- 动态扩展字段用JObject处理
- 自定义转换器处理特殊格式
public class DeviceData { public string DeviceId { get; set; } public DateTime Timestamp { get; set; } [JsonExtensionData] public IDictionary<string, JToken> ExtendedData { get; set; } } // 处理未知字段 if (data.ExtendedData.TryGetValue("voltage", out var voltageToken)) { double voltage = voltageToken.Value<double>(); }遇到的一个坑是设备时间格式不统一,解决方案是写个灵活的时间解析器:
public class FlexibleDateTimeConverter : JsonConverter<DateTime> { private static readonly string[] formats = { "yyyy-MM-ddTHH:mm:ss", "yyyy/MM/dd HH:mm:ss", "yyyyMMddHHmmss" }; public override DateTime ReadJson(/* 参数 */) { string dateString = reader.Value.ToString(); foreach (var format in formats) { if (DateTime.TryParseExact(dateString, format, null, DateTimeStyles.None, out var result)) { return result; } } return DateTime.Parse(dateString); // 最后尝试默认解析 } }