Search code examples
jsonvb.netjson.netdeserializationjson-deserialization

Convert JSON object and array to same class in VB.net


I am trying to convert a JSON returned from a third party API, into VB.NET class object. The problem is that the JSON sometimes returns a node as an Array of objects and sometimes the same node as an single Object. So, while trying to convert when it receives Array of objects, the code throws exception.

    {
  "correlation_id": "228.9219622269229",
  "Error": {
    "messages": [
      {
        "code": "401",
        "description": "Unauthorized"
      }
    ]
  },
  "transaction_status": "Not Processed"
}

As you can see, there is an array of Error messages under Error->messages node. But sometimes, the JSON returned is simpy and object of Error messages something like

{
  "correlation_id": "228.9219622269229",
  "Error": {
    "messages": {
        "code": "401",
        "description": "Unauthorized"
      }
  },
  "transaction_status": "Not Processed"
}

I try to deserialize this JSON to the following class

Public Class PayeezyRefundResponse
    Public correlation_id As String
    Public transaction_status As String
    Public validation_status As String
    Public transaction_type As String
    Public transaction_id As String
    Public transaction_tag As String
    Public bank_resp_code As String
    Public bank_message As String
    Public [Error] As PayeezyError
End Class
Public Class PayeezyError
    Public messages As PayeezyErrorMessages()
End Class
Public Class PayeezyErrorMessages
    Public code As String
    Public description As String
End Class

But when the JSON returns Error message as single object, the code throws exception on PayeezyError class. How can I convert the JSON to this class so that it works in both the cases (i.e. with Array of objects and with single Object) ?


Solution

  • You can build a custom converter that can handle both cases as a List(Of PayeezyErrorMessage).
    This converter always returns a List(Of PayeezyErrorMessage), even when the JSON contains a single object.

    Note: in code, PayeezyErrorMessages, plural, has been renamed to PayeezyErrorMessage, single, since this class generates a single object.

    The custom converter is added to the property as an attribute:

    <JsonConverter(GetType(PayeezyErrorsConverter(Of PayeezyErrorMessage)))>
    Public Messages As List(Of PayeezyErrorMessage)
    

    Refactored code:

    Public Class PayeezyRefundResponse
        ' [...]
        <JsonProperty("Error")>
        Public Errors As PayeezyErrors
    End Class
    
    Public Class PayeezyErrors
        <JsonProperty("messages")>
        <JsonConverter(GetType(PayeezyErrorsConverter(Of PayeezyErrorMessage)))>
        Public Messages As List(Of PayeezyErrorMessage)
    End Class
    
    Public Class PayeezyErrorMessage
        Public code As String
        Public description As String
    End Class
    

    Custom converter:
    ► The writer part is not implemented, since you probably won't need to send back this JSON

    Imports Newtonsoft.Json
    Imports Newtonsoft.Json.Linq
    
    Public Class PayeezyErrorsConverter(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