You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET6 WinForm中Newtonsoft.Json解析JSON序列化异常解决

解决.NET6 WinForm中Newtonsoft.Json解析嵌套JSON数组的序列化异常及字段提取问题

我正在开发.NET6+ WinForm应用,使用Newtonsoft.Json NuGet包解析大型JSON数据,仅需提取ownerName和state字段(state值可为"vacation"或空字符串),且每个用户的状态仅需获取一次(如查询Mattia返回空,D3vil666返回"vacation")。

核心JSON结构如下:

[
   [
      "updateGlobalData",
      {
         "backgroundData":{
            "cities":[
               {
                  "type":"city",
                  "name":"Polis",
                  "id":186979,
                  "level":35,
                  "ownerId":"99253",
                  "ownerName":"D3vil666",
                  "ownerAllyId":"0",
                  "hasTreaties":0,
                  "actions":[],
                  "state":"vacation",
                  "viewAble":1,
                  "infestedByPlague":false
               },
               {
                  "type":"city",
                  "name":"London",
                  "id":378440,
                  "level":28,
                  "ownerId":"242906",
                  "ownerName":"Mattia",
                  "ownerAllyId":"5541",
                  "ownerAllyTag":"LORDS",
                  "hasTreaties":0,
                  "actions":[],
                  "state":"",
                  "viewAble":2,
                  "infestedByPlague":false
               }
            ]
         }
      }
   ]
]

初始代码抛出序列化异常:

Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Ikariam_VM.Form1+GlobalData' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path '[0]', line 1, position 2.'

修改后的代码仍未解决问题,代码如下:

Imports System.Net
Imports System.Net.Http
Imports System.Net.WebRequestMethods
Imports System.Runtime.InteropServices.JavaScript.JSType
Imports System.Text.Json
Imports Microsoft.Web.WebView2.Core
Imports Microsoft.Web.WebView2
Imports Microsoft.Web.WebView2.WinForms
Imports System.Threading
Imports System.IO
Imports System.Runtime.InteropServices.JavaScript
Imports Newtonsoft.Json.Linq
Imports Newtonsoft.Json

Public Class Form1
    Public Class GlobalData
        Public Class BackgroundData
            Public Property Cities As List(Of City)
        End Class

        Public Class City
            Public Property Type As String
            Public Property Name As String
            Public Property OwnerName As String
            Public Property State As String
        End Class
    End Class

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim client As New WebClient()
        Dim jsonString As String = client.DownloadString("https://s10-it.ikariam.gameforge.com/?view=updateGlobalData&islandId=1649&backgroundView=island&currentIslandId=1649&actionRequest=90983fef22b312ff7cbd51f0183bc301&ajax=1")

        Dim jArr = JArray.Parse(jsonString)

        Dim cities = TryCast(jArr(0).OfType(Of JObject)() _
               .Select(Function(p) p.Properties().Where(Function(p) p.Name = "backgroundData").First()) _
               .FirstOrDefault().Value("cities"),
             JArray)

        Dim state As String = Nothing

        If cities IsNot Nothing Then
            state = CStr(cities.Where(Function(c) CStr(c("OwnerName") = "D3vil666")) _
                       .FirstOrDefault()("state"))
        End If
    End Sub
End Class

解决方案

1. 错误原因分析

原JSON是嵌套数组结构:最外层是数组,第一个元素是内部数组,内部数组包含两个元素:字符串updateGlobalData和包含业务数据的对象。之前的代码错误地尝试从jArr(0)中直接筛选JObject,忽略了内部数组的结构,导致无法定位到backgroundData。

另外,代码中的筛选条件CStr(c("OwnerName") = "D3vil666")逻辑错误,应该先获取ownerName字段值再做比较。

2. 修正后的代码

以下代码可以正确解析JSON并提取所需字段,同时实现用户状态去重:

Imports System.Net
Imports Newtonsoft.Json.Linq
Imports Newtonsoft.Json
Imports System.Linq

Public Class Form1
    ' 可选:保留实体类用于强类型解析(也可直接用JToken操作)
    Public Class CityInfo
        Public Property OwnerName As String
        Public Property State As String
    End Class

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        ' 下载JSON数据并自动释放资源
        Using client As New WebClient()
            Dim jsonString As String = client.DownloadString("https://s10-it.ikariam.gameforge.com/?view=updateGlobalData&islandId=1649&backgroundView=island&currentIslandId=1649&actionRequest=90983fef22b312ff7cbd51f0183bc301&ajax=1")

            ' 解析最外层数组
            Dim outerArray = JArray.Parse(jsonString)
            ' 获取内部数组(最外层数组的第一个元素)
            Dim innerArray = outerArray.FirstOrDefault(Function(t) t.Type = JTokenType.Array)
            If innerArray Is Nothing Then Return

            ' 获取内部数组中的业务对象(第二个元素,索引1)
            Dim dataObj = innerArray.ElementAtOrDefault(1)
            If dataObj Is Nothing OrElse dataObj.Type <> JTokenType.Object Then Return

            ' 提取cities数组
            Dim citiesArray = dataObj.SelectToken("backgroundData.cities")
            If citiesArray Is Nothing OrElse citiesArray.Type <> JTokenType.Array Then Return

            ' 方式1:直接用JToken提取并去重
            Dim userStates = citiesArray
                .Select(Function(c) New With {
                    .OwnerName = c.Value(Of String)("ownerName"),
                    .State = c.Value(Of String)("state")
                })
                .GroupBy(Function(u) u.OwnerName)
                .Select(Function(g) g.First()) ' 每个用户只取第一条记录的状态
                .ToList()

            ' 示例:查询指定用户的状态
            Dim targetUser = "D3vil666"
            Dim userState = userStates.FirstOrDefault(Function(u) u.OwnerName = targetUser)?.State
            If userState IsNot Nothing Then
                MessageBox.Show($"{targetUser}的状态:{userState}")
            End If

            ' 方式2:反序列化为实体类(可选,更类型安全)
            Dim cityList = citiesArray.ToObject(Of List(Of CityInfo))()
            Dim distinctUserStates = cityList
                .GroupBy(Function(c) c.OwnerName)
                .Select(Function(g) g.First())
                .ToList()
        End Using
    End Sub
End Class

3. 关键优化点

  • 正确定位JSON结构:通过outerArray.FirstOrDefault获取内部数组,再通过ElementAtOrDefault(1)获取业务对象,避免了数组/对象类型混淆的问题。
  • 字段提取与去重:使用GroupBy按OwnerName分组,取每组第一条记录,确保每个用户的状态仅获取一次。
  • 类型安全处理:通过Value(Of String)或强类型实体类解析字段,避免类型转换错误。
  • 资源释放:使用Using语句包装WebClient,确保资源正确释放。

内容的提问来源于stack exchange,提问作者Mattia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 10:26:04