How to deserialize JSON which can be an array or a single object

前端 未结 4 1029
迷失自我
迷失自我 2021-01-13 04:45

I\'m fairly new to using JSON.net and having trouble with some json I\'m getting which sometime comes in as an array and sometimes as single object. Here is an example of wh

相关标签:
4条回答
  • 2021-01-13 05:17

    Thanks to both crowcoder & Kundan. I combined the two approaches and came up with something that works with both json inputs. Here is the final code.

    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
    

    Big thanks for the quick replies. This solved something I'd been spending a lot time off-and-on trying to figure out.

    0 讨论(0)
  • 2021-01-13 05:18

    You could achieve this to modify your jsonCar class like below

    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
    

    And the code should be like below:

    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
    
    0 讨论(0)
  • 2021-01-13 05:29

    You can generically deserialize to Object and then inspect what you have. You can check for a JArray or JObject and act accordingly. You don't even need to deserialize into a specific type, you can work with the Dynamic objects but that may not be the best idea.

        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
    
    0 讨论(0)
  • 2021-01-13 05:32

    Here is how to get the SingleOrArrayConverter solution in the linked duplicate question working for your use case.

    First, here is the VB-translated converter code. Take this and save it to a class file somewhere in your project. You can then easily reuse it for any future cases like this.

    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
    

    Now that you have this converter, any time you have a property that can be either a list or a single item, all you have to do is declare it as a list in your class and then annotate that list with a JsonConverter attribute such that it uses the SingleOrArrayConverter class. In your case, that would look like this:

    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
    

    Then, just deserialize as you normally would, and it works as expected.

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

    Here is a complete demonstration: https://dotnetfiddle.net/msYNeQ

    0 讨论(0)
提交回复
热议问题