反序列化 JSON 字符串,它是数组并且具有非法的 属性 名称

Deserialize JSON string which is array and has illegal property names

我正在使用 JSON.NET 反序列化来自 HTTP 查询的 JSON 响应,但我在反序列化时遇到问题。

来源JSON是这样的:

[
  {
    "type": "rpc",
    "tid": 18,
    "action": "TaskSystem",
    "method": "createTask",
    "result": {
      "0": {
        "success": true,
        "successes": [
          [
            "Task successfuly created with S/N #22920"
          ]
        ]
      },
      "1": {
        "success": true,
        "successes": [
          [
            "Task successfuly created with S/N #22921"
          ],
          "Task #22921 marked as urgent"
        ]
      },
      "records": [
        {
          "id": 22920
        },
        {
          "id": 22921
        }
      ],
      "success": true
    }
  }
]

我一直在使用这些 classes 进行反序列化:

Private Sub Deserialize()
    Dim Jobj = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Response())(Jstring)
End Sub

Public Class Record
    Public Property id As Integer
End Class

Public Class Result
    Public Property records As Record()
    Public Property success As Boolean
End Class

Public Class Response
    Public Property type As String
    Public Property tid As Integer
    Public Property action As String
    Public Property method As String
    Public Property result As Result
End Class

但是我丢失了查询返回的 success/failure 消息

我应该如何编写 class Result 以收集属性 records As Record(), succes As Boolean and also 那些名为“0”、“1”等的对象...?

非常感谢您的帮助。

您可以将 JSON 复制到剪贴板并使用
编辑 -> 选择性粘贴 -> 粘贴 JSon 为 类
从 classes 中获取粗略信息。所有机器人都弄错了部分内容,例如:

' wrong
Public Property successes()() As String
' correct:
Public Property successes As String()()

这似乎有效:

Public Class StatusS          ' string()() version
    Public Property success As Boolean
    Public Property successes As List(Of List(Of String))
End Class

Public Class StatusO         ' Object() version
    Public Property success As Boolean
    Public Property successes As List(Of Object)
End Class

机器人也很难使用非法的 属性 名称,尤其是 VB,那里有很多关键字(EndError 很常见)。使用 JsonProperty 创建别名(将“0”或“1”映射到合法的内容):

Public Class Result
    <JsonProperty("0")>
    Public Property StatusA As StatusS
    <JsonProperty("1")>
    Public Property StatusB As StatusO
    Public Property records As List(Of Record)
    Public Property success As Boolean
End Class

只要他们提供数组,您就可以使用 List(Of T)

Dim jData = JsonConvert.DeserializeObject(Of Response())(jstr)

Console.WriteLine(jData(0).action)
Console.WriteLine(jData(0).result.records(0).id)
Console.WriteLine(jData(0).result.StatusA.success)
Console.WriteLine(jData(0).result.StatusA.successes(0)(0).ToString)
Console.WriteLine(jData(0).result.StatusB.successes(0).ToString)

结果:

TaskSystem
22920
True
Task successfuly created with S/N #22920
[
"Task successfuly created with S/N #22921"
]

很难发现,但是 [ "Task successfuly ... #22920" ] 之后的 missing/extra 逗号导致 Object 版本与 String()() 一个或另一个可能是错字。如果没有,您可以为这两者编写一个转换器,或者为 Q+D 的 StatusO class 添加一个方法来删除括号:

Friend Function GetSuccessMsg(ndx As Int32) As String
    If ndx < successes.Count Then
        Return successes(ndx).ToString().
            Replace("[", "").Replace("]", "").
            Replace("""", "").Trim()
    End If
    Return String.Empty
End Function

Console.WriteLine(jData(0).result.StatusA.GetSuccessMsg(0))

Task successfuly created with S/N #22921

这里有两个不相关的问题:

  1. 您的 Result class 由一组固定属性和一组可变属性组成,这些属性具有递增的数字名称和标准化的值架构。您希望自动反序列化标准属性并捕获和反序列化自定义属性。

    这可以使用 JsonExtensionData. With this attribute, you can temporarily deserialize the custom properties to a Dictionary(of String, JToken) then later convert to a Dictionary(Of String, Success) in an [OnDeserialized] 回调来完成。这里 Success 是一种旨在捕获 JSON 的类型,如下所示:

    {
      "success": true,
      "successes": [ [ "Task successfuly created with S/N #22920" ] ]
    }
    

    有关文档,请参阅 Deserialize ExtensionData

  2. 在上述 Success 类型中,"successes" 数组包含字符串数组和单个字符串。

    如果您定义 successes 属性 如下:

    Public Property successes As List(Of List(Of String))
    

    然后可以使用 How to handle both a single item and an array for the same property using JSON.net, setting it as the item converterSingleOrArrayConverter(Of String) 变体通过 <JsonProperty(ItemConverterType := GetType(SingleOrArrayConverter(Of String)))>.

  3. 来处理

因此你的最终 classes 看起来像:

Public Class Success
    Public Property success As Boolean
    <JsonProperty(ItemConverterType := GetType(SingleOrArrayConverter(Of String)))> _   
    Public Property successes As List(Of List(Of String))
End Class

Public Class Record
    Public Property id As Integer
End Class

Public Class Result
    Public Property records As Record()
    Public Property success As Boolean

    <JsonIgnore> _
    Public Property successes as Dictionary(Of string, Success)

    <JsonExtensionData> _
    Private _additionalData as Dictionary(Of string, JToken)

    <System.Runtime.Serialization.OnSerializing> _
    Sub OnSerializing(ByVal context as System.Runtime.Serialization.StreamingContext)
        If successes IsNot Nothing
            _additionalData = successes.ToDictionary(Function(p) p.Key, Function(p) JToken.FromObject(p.Value))
        Else
            _additionalData = Nothing
        End If
    End Sub

    <System.Runtime.Serialization.OnSerialized> _
    Sub OnSerialized(ByVal context as System.Runtime.Serialization.StreamingContext)
        _additionalData = Nothing
    End Sub

    <System.Runtime.Serialization.OnDeserializing> _
    Sub OnDeserializing(ByVal context as System.Runtime.Serialization.StreamingContext)
        _additionalData = Nothing
    End Sub

    <System.Runtime.Serialization.OnDeserialized>
    Sub OnDeserialized(ByVal context as System.Runtime.Serialization.StreamingContext)

        If _additionalData IsNot Nothing
            successes = _additionalData.ToDictionary(Function(p) p.Key, Function(p) p.Value.ToObject(Of Success)())
        End If
        _additionalData = Nothing

    End Sub
End Class

Public Class Response
    Public Property type As String
    Public Property tid As Integer
    Public Property action As String
    Public Property method As String
    Public Property result As Result
End Class

Public Class SingleOrArrayConverter(Of T)
    Inherits JsonConverter

    Public Overrides ReadOnly Property CanWrite() As Boolean
        Get
            Return false
        End Get
    End Property

    Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
        Throw New NotImplementedException()
    End Sub

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim retVal As Object = New List(Of T)()
        If reader.TokenType = JsonToken.StartArray Then
            serializer.Populate(reader, retVal)
        Else
            Dim instance As T = DirectCast(serializer.Deserialize(reader, GetType(T)), T)
            retVal.Add(instance)
        End If
        Return retVal
    End Function

    Public Overrides Function CanConvert(objectType As Type) As Boolean
        Return objectType = GetType(List(Of T))
    End Function

End Class

原型fiddle.