VB.NET中Newtonsoft.Json反序列化JSON数组集合报错咨询
问题描述
使用Newtonsoft.Json在VB.NET中反序列化JSON时抛出以下错误:
Newtonsoft.Json.JsonSerializationException:
'Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type 'System.Collections.Generic.List`1[]' because the type
requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix
this error either change the JSON to a JSON array (e.g. [1,2,3]) or
change the deserialized type so that it is a normal .NET type (e.g.
not a primitive type like integer, not a collection type like an array
or List) that can be deserialized from a JSON object.
JsonObjectAttribute can also be added to the type to force it to
deserialize from a JSON object.
目前通过循环调用多个selectToken实现功能,但性能较差,需要优化。
现有类定义
Public Class Orders Public recordSetTotal As Integer Public Class ordersList 'orders list is an array in the JSON data source Public Class orderDetails Public grandTotal As Single Public placedDate As String End Class Public Class address Public postcode As String Public addressLine() As String 'this is an array in the JSON data source End Class End Class End Class
当前反序列化代码
'orderData is the JSON string block Dim ordersList As List(Of Orders) = JsonConvert.DeserializeObject(Of List(Of Orders))(orderData)
示例JSON数据
{ "recordSetTotal":10, "ordersList":[ { "orderDetails":{ "grandTotal":50.0000, "placedDate":"2022-12-25 12:00:00.000" }, "addressLine":[ "123 Fake St", "Somewhere", "ABG" ] } ] }
解决方案
1. 类结构必须调整:修正嵌套关系
现有类结构和JSON数据完全不匹配,这是反序列化失败的核心原因:
- JSON根对象是一个包含
recordSetTotal和ordersList数组的单个对象,而非List(Of Orders)数组 - 原
Orders类中的ordersList是嵌套类,但JSON里ordersList是数组,每个元素包含orderDetails和addressLine,不存在单独的address对象
需要拆解不必要的嵌套,让类结构和JSON层级一一对应:
' 对应JSON根对象 Public Class OrderRoot Public recordSetTotal As Integer ' 用List更灵活,和JSON数组对应;数组写法两种都可以,见下文说明 Public ordersList As List(Of OrderItem) End Class ' 对应ordersList数组中的每个元素 Public Class OrderItem Public orderDetails As OrderDetail Public addressLine() As String End Class ' 对应orderDetails对象 Public Class OrderDetail Public grandTotal As Single Public placedDate As String End Class
2. 两种数组声明写法完全等价
你提到的两种数组声明方式:
Public ordersList() As ordersList
和
Public ordersList As ordersList()
在VB.NET中是完全等价的,仅语法写法不同,功能没有区别。不过更推荐使用List(Of T)泛型集合,它比数组更灵活(支持动态增删元素),Newtonsoft.Json对泛型集合的支持也更完善。
3. 修正后的反序列化代码
用调整后的类结构反序列化,就能直接得到完整的对象模型,性能远高于循环调用selectToken:
Dim orderRoot As OrderRoot = JsonConvert.DeserializeObject(Of OrderRoot)(orderData) ' 直接访问数据示例:获取第一个订单的总价 Dim firstTotal = orderRoot.ordersList(0).orderDetails.grandTotal
内容的提问来源于stack exchange,提问作者Dan

