1

I want to deserialize the response of a HTTP-Request to Objects.
The Response looks like this:

[
    "BeginOfEnumerable",
    [
        {
            "StatusID": 12345,
            "ItemID": 987654
        }
    ],
    "EndOfEnumerable"
]

I am using Newtonsoft.Json.

I tried to use Visual Studio's Edit > Paste special > Paste JSON as Classes to create the class model, but the result looks strange to me:

Public Class Rootobject
        Public Property Property1() As Object
End Class

Also, this throws an Exception:

Dim myObject As Rootobject = JsonConvert.DeserializeObject(response.Content)

» Unexpected character encountered while parsing value: .Path ", line 0, position 0.

I want to get the StatusID and ItemID.
A JSON Validator I used says this JSON is valid.

1
  • If I do Dim myObject As Rootobject = JsonConvert.DeserializeObject(response.Content) I get an System.InvalidCastException: Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'Rootobject'. And if I just do JsonConvert.DeserializeObject(response.Content) using the JSON string shown, there is no exception, see dotnetfiddle.net/5j88N1. Can you share a minimal reproducible example, or at least the full ToString() output of the exception including the exception type, message and inner exception(s) if any? Commented Mar 12, 2021 at 14:31

1 Answer 1

1

The JSON structure is valid, per se, you may have some difficulty with the strings that don't follow the name:value pattern.

You could deserialize only the inner array of values, as a List(class), e.g.,

Imports System.Collections.Generic
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Public Class EnumObject
    Public Property StatusId As Long
    Public Property ItemId As Long
End Class

'[...]

Dim jsonEnumsTokens = JArray.Parse(response.Content)
Dim enumsArray = jsonEnumsTokens.Children.Skip(1).First().ToObject(Of List(Of EnumObject))()

which returns the list of EnumObject that contain the values, if that's all you're interested in and you don't need to handle this JSON in any other way, or serialize it back in the same form.

If you want to get the JSON representation of the arrays only:

Dim justTheArraysJson = jsonEnumsTokens.Children.Skip(1).First().ToString()

In case you want to perform deserialization and serialization, maintaining the structure, you could use a custom JsonConverter that handles this case.

The EnumObjectsConverter creates an intermediate structure that contains both the single strings and the array of values, contained in the EnumObjectArray property.
This also allows to serialize back to the original structure, if needed.

Call it as:

Dim enumArray = EnumObjectsHandler.Deserialize(response.Content)
Dim serialized = EnumObjectsHandler.Serialize(enumArray)

The EnumObjectsHandler class that provides the static methods for the serialization:

Public Class EnumObjectsHandler
    Private Shared settings As JsonSerializerSettings = New JsonSerializerSettings() With {
        .Converters = {New EnumObjectsConverter()}
    }
    Public Shared Function Deserialize(json As String) As List(Of EnumObjectsRoot)
        Return JsonConvert.DeserializeObject(Of List(Of EnumObjectsRoot))(json, settings)
    End Function

    Public Shared Function Serialize(data As List(Of EnumObjectsRoot)) As String
        Return JsonConvert.SerializeObject(data, settings)
    End Function

    Public Class EnumObject
        Public Property StatusId As Long
        Public Property ItemId As Long
    End Class

    Public Structure EnumObjectsRoot
        Public Property EnumObjectArray As List(Of EnumObject)
        Public Property Operation As String

        Public Shared Widening Operator CType(objectArray As List(Of EnumObject)) As EnumObjectsRoot
            Return New EnumObjectsRoot With {.EnumObjectArray = objectArray}
        End Operator

        Public Shared Widening Operator CType(op As String) As EnumObjectsRoot
            Return New EnumObjectsRoot With {.Operation = op}
        End Operator
    End Structure


    Friend Class EnumObjectsConverter
        Inherits JsonConverter

        Public Overrides Function CanConvert(t As Type) As Boolean
            Return t Is GetType(EnumObjectsRoot)
        End Function

        Public Overrides Function ReadJson(reader As JsonReader, t As Type, existingValue As Object, serializer As JsonSerializer) As Object
            Select Case reader.TokenType
                Case JsonToken.String
                    Dim stringValue = serializer.Deserialize(Of String)(reader)
                    Return New EnumObjectsRoot() With {
                        .Operation = stringValue
                    }
                Case JsonToken.StartArray
                    Dim arrayValue = serializer.Deserialize(Of List(Of EnumObject))(reader)
                    Return New EnumObjectsRoot() With {
                        .EnumObjectArray = arrayValue
                    }
            End Select
            Throw New Exception("EnumObjectsRoot could not be deserialized")
        End Function

        Public Overrides Sub WriteJson(writer As JsonWriter, untypedValue As Object, serializer As JsonSerializer)
            Dim value = CType(untypedValue, EnumObjectsRoot)
            If value.Operation IsNot Nothing Then
                serializer.Serialize(writer, value.Operation)
                Return
            End If
            If value.EnumObjectArray IsNot Nothing Then
                serializer.Serialize(writer, value.EnumObjectArray)
                Return
            End If
            Throw New Exception("EnumObjectsRoot could not be serialized")
        End Sub
    End Class
End Class
Sign up to request clarification or add additional context in comments.

3 Comments

Rather than deserializing the string returned by First().ToString() it would better to use JToken.ToObject() to deserialize deserialize directly without the intermediate string formatting and re-parsing, i.e. jsonEnumsTokens.Children.Skip(1).First().ToObject(Of List(Of EnumObject))()
@dbc Sure, Ive added the edit. I often use that form posting here, because I've noticed people want to see what the JSON string content is at that point, as in string justTheEnumsJson = jsonEnumsTokens.Children.Skip(1).First().ToString();. I'll probably drop it, it's not GC-friendly, if one just coy/pastes this in real code.
Thank you so much for your efforts! :) » The problem was a preceding space character in the response of the HTTP-Request

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.