如何反序列化可以是数组或单个对象的JSON



我对使用JSON.net相当陌生,并且在使用一些JSON时遇到了问题,这些JSON有时是作为数组出现的,有时是作为单个对象出现的。下面是我在json 中看到的一个例子

有一种方式。。。

{
  "Make": "Dodge",
  "Model": "Charger",
  "Lines": [
    {
      "line": "base",
      "engine": "v6",
      "color": "red"
    },
    {
      "line": "R/T",
      "engine": "v8",
      "color": "black"
    }
  ],
  "Year": "2013"
}

另一种可能出现在中的方式

{
  "Make": "Dodge",
  "Model": "Charger",
  "Lines": {
    "line": "base",
    "engine": "v6",
    "color": "red"
  },
  "Year": "2013"
}

以下是我一直在使用的代码,它在第一种情况下工作,在第二种情况下抛出异常。我一直在网上搜索实现这一点的方法,我真的被卡住了。

Public Class jsonCar
    Public Property make As String
    Public Property model As String
    Public Property lines As List(Of jsonCarLines)
    Public Property year As String
End Class
Public Class jsonCarLines
    Public Property line As String
    Public Property engine As String
    Public Property color As String
End Class
Module Module1
    Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": [{""line"":""base"",""engine"": ""v6"",""color"":""red""},{""line"":""R/T"",""engine"":""v8"",""color"":""black""}],""Year"":""2013""}"
    'Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": {""line"":""R/T"",""engine"":""v8"",""color"":""black""},""Year"":""2013""}"
    Sub Main()
        Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)
        Console.WriteLine("Make: " & car.make)
        Console.WriteLine("Model: " & car.model)
        Console.WriteLine("Year: " & car.year)
        Console.WriteLine("Lines: ")
        For Each ln As jsonCarLines In car.lines
            Console.WriteLine("    Name: " & ln.line)
            Console.WriteLine("    Engine: " & ln.engine)
            Console.WriteLine("    Color: " & ln.color)
            Console.WriteLine()
        Next
        Console.ReadLine()
    End Sub
End Module

我猜这可能需要一个自定义的JsonConverter,但我有点不知道如何设置。

以下是如何使链接的重复问题中的SingleOrArrayConverter解决方案适用于您的用例。

首先,这里是VB翻译的转换器代码。将其保存到项目中某个类文件中。然后,您可以在将来的任何类似情况下轻松地重用它。

Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class SingleOrArrayConverter(Of T)
    Inherits JsonConverter
    Public Overrides Function CanConvert(objectType As Type) As Boolean
        Return objectType = GetType(List(Of T))
    End Function
    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim token As JToken = JToken.Load(reader)
        If (token.Type = JTokenType.Array) Then
            Return token.ToObject(Of List(Of T))()
        End If
        Return New List(Of T) From {token.ToObject(Of T)()}
    End Function
    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
End Class

既然有了这个转换器,任何时候只要有一个可以是列表或单个项的属性,所要做的就是在类中将其声明为列表,然后用JsonConverter属性注释该列表,使其使用SingleOrArrayConverter类。在你的情况下,看起来是这样的:

Public Class jsonCar
    Public Property make As String
    Public Property model As String
    <JsonConverter(GetType(SingleOrArrayConverter(Of jsonCarLines)))>
    Public Property lines As List(Of jsonCarLines)
    Public Property year As String
End Class

然后,只需像往常一样进行反序列化,它就会按预期工作。

Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)

以下是一个完整的演示:https://dotnetfiddle.net/msYNeQ

您可以通过如下修改jsonCar类来实现这一点

Public Class jsonCar
    Public Property make As String 
    Public Property model As String
    Public Property linesCollection As List(Of jsonCarLines) // Change name
    Public Property lines As String // Change the type to string
    Public Property year As String
End Class

代码应该如下所示:

Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)
If (car.lines.StartsWith("[")) Then
    car.linesCollection = JsonConvert.DeserializeObject(List(Of jsonCarLines))(car.lines)
Else
    car.linesCollection = new List(Of jsonCarLines)
    car.linesCollection.Add(JsonConvert.DeserializeObject(Of jsonCarLines)(car.lines))
EndIf

感谢crowcoder&昆丹。我将这两种方法结合起来,提出了一种可以同时使用json输入的方法。这是最后的代码。

Public Class jsonCar
    Public Property make As String
    Public Property model As String
    Public Property linesArray As List(Of jsonCarLines)
    Public Property year As String
End Class
Public Class jsonCarLines
    Public Property line As String
    Public Property engine As String
    Public Property color As String
End Class
Module Module1
    'Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": [{""line"":""base"",""engine"": ""v6"",""color"":""red""},{""line"":""R/T"",""engine"":""v8"",""color"":""black""}],""Year"":""2013""}"
    Private Const json As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": {""line"":""R/T"",""engine"":""v8"",""color"":""black""},""Year"":""2013""}"
    Sub Main()
        Dim obj As JObject = JsonConvert.DeserializeObject(json)
        Dim ln As JToken = obj("Lines")
        Dim car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)
        If (ln.GetType() Is GetType(Newtonsoft.Json.Linq.JArray)) Then
            car.linesArray = JsonConvert.DeserializeObject(Of List(Of jsonCarLines))(JsonConvert.SerializeObject(ln))
        End If
        If (ln.GetType() Is GetType(Newtonsoft.Json.Linq.JObject)) Then
            car.linesArray = New List(Of jsonCarLines)
            car.linesArray.Add(JsonConvert.DeserializeObject(Of jsonCarLines)(JsonConvert.SerializeObject(ln)))
        End If
        Console.WriteLine("Make: " & car.make)
        Console.WriteLine("Model: " & car.model)
        Console.WriteLine("Year: " & car.year)
        Console.WriteLine("Lines: ")
        For Each line As jsonCarLines In car.linesArray
            Console.WriteLine("    Name: " & line.line)
            Console.WriteLine("    Engine: " & line.engine)
            Console.WriteLine("    Color: " & line.color)
            Console.WriteLine()
        Next
        Console.ReadLine()
    End Sub
End Module

非常感谢您的快速回复。这解决了我花了很多时间试图弄清楚的问题。

您可以一般地反序列化为Object,然后检查您所拥有的内容。您可以检查JArray或JObject并采取相应的操作。您甚至不需要反序列化为特定的类型,您可以使用Dynamic对象,但这可能不是最好的主意。

    Module Module1
    Sub Main()
     Dim jsonWithArray As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": [{""line"":""base"",""engine"": ""v6"",""color"":""red""},{""line"":""R/T"",""engine"":""v8"",""color"":""black""}],""Year"":""2013""}"
     Dim jsonWithObject As String = "{""Make"":""Dodge"",""Model"":""Charger"",""Lines"": {""line"":""base"",""engine"": ""v6"",""color"":""red""},""Year"":""2013""}"
     Dim witharray As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonWithArray)
     Dim withstring As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonWithObject)
     Dim jtokArray As Newtonsoft.Json.Linq.JToken = witharray("Lines")
     Dim jtokStr As Newtonsoft.Json.Linq.JToken = withstring("Lines")
     If (jtokArray.GetType() Is GetType(Newtonsoft.Json.Linq.JArray)) Then
        Console.WriteLine("its an array")
     End If
     If (jtokStr.GetType() Is GetType(Newtonsoft.Json.Linq.JObject)) Then
        Console.WriteLine("its an object")
     End If
    Console.ReadKey()
    End Sub
End Module

相关内容

  • 没有找到相关文章

最新更新