首页 > 解决方案 > 如何使用 Json.Net 反序列化嵌套的 JSON 数组?

问题描述

我想反序列化 HTTP 请求对对象的响应。
响应如下所示:

[
    "BeginOfEnumerable",
    [
        {
            "StatusID": 12345,
            "ItemID": 987654
        }
    ],
    "EndOfEnumerable"
]

我正在使用 Newtonsoft.Json。

我尝试使用 Visual StudioEdit > Paste special > Paste JSON as Classes创建类模型,但结果对我来说看起来很奇怪:

Public Class Rootobject
        Public Property Property1() As Object
End Class

此外,这会引发异常:

Dim myObject As Rootobject = JsonConvert.DeserializeObject(response.Content)

» 解析值时遇到意外字符:.Path ",第 0 行,第 0 位置。

我想得到StatusIDand ItemID
我使用的 JSON Validator 说这个 JSON 是有效的。

标签: jsonvb.netvisual-studiojson.netdeserialization

解决方案


JSON 结构本身是有效的,对于不遵循该name:value模式的字符串,您可能会遇到一些困难。

您只能反序列化值的内部数组List(class),例如,

Imports System.Collections.Generic
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Public Class EnumObject
    Public Property StatusId As Long
    Public Property ItemId As Long
End Class

'[...]

Dim jsonEnumsTokens = JArray.Parse(response.Content)
Dim enumsArray = jsonEnumsTokens.Children.Skip(1).First().ToObject(Of List(Of EnumObject))()

它返回EnumObject包含值的列表,如果这就是您感兴趣的全部并且您不需要以任何其他方式处理此 JSON,或者以相同的形式将其序列化回。

如果您只想获取数组的 JSON 表示形式:

Dim justTheArraysJson = jsonEnumsTokens.Children.Skip(1).First().ToString()

如果您想执行反序列化和序列化,维护结构,您可以使用处理这种情况的自定义 JsonConverter 。

EnumObjectsConverter创建一个中间结构,其中包含EnumObjectArray属性中包含的单个字符串和值数组。
如果需要,这也允许序列化回原始结构。

称它为:

Dim enumArray = EnumObjectsHandler.Deserialize(response.Content)
Dim serialized = EnumObjectsHandler.Serialize(enumArray)

为序列化提供静态方法的EnumObjectsHandler类:

Public Class EnumObjectsHandler
    Private Shared settings As JsonSerializerSettings = New JsonSerializerSettings() With {
        .Converters = {New EnumObjectsConverter()}
    }
    Public Shared Function Deserialize(json As String) As List(Of EnumObjectsRoot)
        Return JsonConvert.DeserializeObject(Of List(Of EnumObjectsRoot))(json, settings)
    End Function

    Public Shared Function Serialize(data As List(Of EnumObjectsRoot)) As String
        Return JsonConvert.SerializeObject(data, settings)
    End Function

    Public Class EnumObject
        Public Property StatusId As Long
        Public Property ItemId As Long
    End Class

    Public Structure EnumObjectsRoot
        Public Property EnumObjectArray As List(Of EnumObject)
        Public Property Operation As String

        Public Shared Widening Operator CType(objectArray As List(Of EnumObject)) As EnumObjectsRoot
            Return New EnumObjectsRoot With {.EnumObjectArray = objectArray}
        End Operator

        Public Shared Widening Operator CType(op As String) As EnumObjectsRoot
            Return New EnumObjectsRoot With {.Operation = op}
        End Operator
    End Structure


    Friend Class EnumObjectsConverter
        Inherits JsonConverter

        Public Overrides Function CanConvert(t As Type) As Boolean
            Return t Is GetType(EnumObjectsRoot)
        End Function

        Public Overrides Function ReadJson(reader As JsonReader, t As Type, existingValue As Object, serializer As JsonSerializer) As Object
            Select Case reader.TokenType
                Case JsonToken.String
                    Dim stringValue = serializer.Deserialize(Of String)(reader)
                    Return New EnumObjectsRoot() With {
                        .Operation = stringValue
                    }
                Case JsonToken.StartArray
                    Dim arrayValue = serializer.Deserialize(Of List(Of EnumObject))(reader)
                    Return New EnumObjectsRoot() With {
                        .EnumObjectArray = arrayValue
                    }
            End Select
            Throw New Exception("EnumObjectsRoot could not be deserialized")
        End Function

        Public Overrides Sub WriteJson(writer As JsonWriter, untypedValue As Object, serializer As JsonSerializer)
            Dim value = CType(untypedValue, EnumObjectsRoot)
            If value.Operation IsNot Nothing Then
                serializer.Serialize(writer, value.Operation)
                Return
            End If
            If value.EnumObjectArray IsNot Nothing Then
                serializer.Serialize(writer, value.EnumObjectArray)
                Return
            End If
            Throw New Exception("EnumObjectsRoot could not be serialized")
        End Sub
    End Class
End Class

推荐阅读