如何将嵌套JSON数组正确反序列化为CandlestickData对象数组?
这个问题我之前也碰到过,核心是对[JsonArray]特性的用法理解偏差导致的,咱们一步步拆解解决:
问题根源
你提供的JSON是数组嵌套数组的结构:外层是K线数据集合,每个K线是包含12个元素的数组。而你给CandlestickData类添加了[JsonArray]特性——这个特性的作用是告诉Json.NET把单个类实例序列化成JSON数组,反过来,当你反序列化时,Json.NET会误以为CandlestickData是一个集合类型,试图去创建并填充它,但它本质是普通POCO类,所以抛出了Cannot create and populate list type CandlestickData错误。
另外,你尝试把decimal改成string也没用,因为错误的根源不是类型匹配,而是类的特性标记错误。
正确解决方案
方案1:移除[JsonArray],用构造函数匹配数组顺序
这是最贴合Newtonsoft.Json设计的方式,移除类上的[JsonArray],然后添加一个参数顺序和JSON数组元素完全一致的构造函数,再加上[JsonConstructor]特性让Json.NET识别它:
public class CandlestickData { public long OpenTime { get; set; } public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } public decimal Close { get; set; } public decimal Volume { get; set; } public long CloseTime { get; set; } public decimal QuoteAssetVolume { get; set; } public long NumberOfTrades { get; set; } public decimal TakerBuyBaseAssetVolume { get; set; } public decimal TakerBuyQuoteAssetVolume { get; set; } public decimal Ignore { get; set; } // 构造函数参数顺序严格对应JSON数组的12个元素 [JsonConstructor] public CandlestickData( long openTime, decimal open, decimal high, decimal low, decimal close, decimal volume, long closeTime, decimal quoteAssetVolume, long numberOfTrades, decimal takerBuyBaseAssetVolume, decimal takerBuyQuoteAssetVolume, decimal ignore) { OpenTime = openTime; Open = open; High = high; Low = low; Close = close; Volume = volume; CloseTime = closeTime; QuoteAssetVolume = quoteAssetVolume; NumberOfTrades = numberOfTrades; TakerBuyBaseAssetVolume = takerBuyBaseAssetVolume; TakerBuyQuoteAssetVolume = takerBuyQuoteAssetVolume; Ignore = ignore; } }
之后直接用你原来的反序列化代码就行,完全不需要改动:
var candlesticks = JsonConvert.DeserializeObject<CandlestickData[]>(content);
方案2:手动映射原始数组(适合不想写构造函数的场景)
如果觉得写构造函数麻烦,可以先把JSON反序列化成原始的二维数组,再手动映射到CandlestickData对象:
// 先反序列化为原始的object二维数组 var rawKlineData = JsonConvert.DeserializeObject<object[][]>(content); // 逐个映射到CandlestickData var candlesticks = rawKlineData.Select(kline => new CandlestickData { OpenTime = Convert.ToInt64(kline[0]), Open = Convert.ToDecimal(kline[1]), High = Convert.ToDecimal(kline[2]), Low = Convert.ToDecimal(kline[3]), Close = Convert.ToDecimal(kline[4]), Volume = Convert.ToDecimal(kline[5]), CloseTime = Convert.ToInt64(kline[6]), QuoteAssetVolume = Convert.ToDecimal(kline[7]), NumberOfTrades = Convert.ToInt64(kline[8]), TakerBuyBaseAssetVolume = Convert.ToDecimal(kline[9]), TakerBuyQuoteAssetVolume = Convert.ToDecimal(kline[10]), Ignore = Convert.ToDecimal(kline[11]) }).ToArray();
这种方式更直观,缺点是需要手动写每个属性的转换逻辑,适合属性数量不多的场景。
验证测试
用你的JSON字符串测试方案1,会成功生成3个CandlestickData实例,每个属性都能正确赋值,比如第一个实例的OpenTime是1627948800000,Open是33361.77m,完全符合JSON里的数据。
内容的提问来源于stack exchange,提问作者Ron Splinter

