Freemo Freemo - 1 year ago 99 Question

Deserializing JSON in Visual Basic .NET

Wondering if you could help me create a VB.Net class into which I can deserialize the following JSON response:


I have the following:

Class mLikes

Public actors As IList(Of IList(Of String))

end Class


Class Player

Public Id As Integer

Public Name As String

Public Likes As mLikes

Public Code As Integer

End Class

I am using Newtonsoft.Json to deserialize:

Result = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Player)(jsonResponse)

If I know that the actors elements always follow the same format -

Class Actor
Public Name as String
Public NumberOfMovies as Integer
Public NumberOfAwards as Integer
Public NumberOfTVshows as Integer
End Class

Is there a way I can parse the JSON response so that Player.Likes.Actors is a List(Of Actor) instead of a List(Of List(Of String)) which is what I have now?

dbc dbc
Answer Source

What you can do is to create a JsonConverter that serializes your Actor class as an IEnumerable<object> in the correct order, then in deserialization reads the JSON using LINQ to JSON, checks that the token read is an array, then sets the properties in the equivalent order.

You could hardcode this for your Actor class, but I think it's more interesting to create a generic converter that converts non-enumerable POCO from and to a JSON array using the POCO type's property order. This order can be specified in your class using the <JsonProperty(Order := NNN)> attribute.

Thus, the converter:

Public Class ObjectToArrayConverter(Of T)
    Inherits JsonConverter

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

    Private Shared Function ShouldSkip(p As JsonProperty) As Boolean
        Return p.Ignored Or Not p.Readable Or Not p.Writable
    End Function

    Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
        If value Is Nothing Then
            Dim type = value.GetType()
            Dim contract = TryCast(serializer.ContractResolver.ResolveContract(type), JsonObjectContract)
            If contract Is Nothing Then
                Throw New JsonSerializationException("invalid type " & type.FullName)
            End If
            Dim list = contract.Properties.Where(Function(p) Not ShouldSkip(p)).Select(Function(p) p.ValueProvider.GetValue(value))
            serializer.Serialize(writer, list)
        End If
    End Sub

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim token = JToken.Load(reader)
        If token Is Nothing OrElse token.Type = JTokenType.Null Then
            Return Nothing
        End If
        If token.Type <> JTokenType.Array Then
            Throw New JsonSerializationException("token was not an array")
        End If
        Dim contract = TryCast(serializer.ContractResolver.ResolveContract(objectType), JsonObjectContract)
        If contract Is Nothing Then
            Throw New JsonSerializationException("invalid type " & objectType.FullName)
        End If
        Dim value = If(existingValue, contract.DefaultCreator()())
        For Each pair In contract.Properties.Where(Function(p) Not ShouldSkip(p)).Zip(token, Function(p, v) New With { Key.Value = v, Key.Property = p })
            Dim propertyValue = pair.Value.ToObject(pair.Property.PropertyType, serializer)
            pair.Property.ValueProvider.SetValue(value, propertyValue)
        Return value
    End Function
End Class

And your class:

<JsonConverter(GetType(ObjectToArrayConverter(Of Actor)))> _
Public Class Actor
    ' Use [JsonProperty(Order=x)] // to explicitly set the order of properties
    <JsonProperty(Order := 0)> _
    Public Property Name As String

    <JsonProperty(Order := 1)> _
    Public Property NumberOfMovies As Integer

    <JsonProperty(Order := 2)> _
    Public Property NumberOfAwards As Integer

    <JsonProperty(Order := 3)> _
    Public Property NumberOfTVshows As Integer
End Class

Working fiddle.