I'm using JSON.NET to deserialize JSON responses from HTTP queries, but I have a problem on deserialization.
The source JSON is like that:
[
{
"type": "rpc",
"tid": 18,
"action": "TaskSystem",
"method": "createTask",
"result": {
"0": {
"success": true,
"successes": [
[
"Task successfuly created with S/N #22920"
]
]
},
"1": {
"success": true,
"successes": [
[
"Task successfuly created with S/N #22921"
],
"Task #22921 marked as urgent"
]
},
"records": [
{
"id": 22920
},
{
"id": 22921
}
],
"success": true
}
}
]
I've been using these classes for deserialization:
Private Sub Deserialize()
Dim Jobj = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Response())(Jstring)
End Sub
Public Class Record
Public Property id As Integer
End Class
Public Class Result
Public Property records As Record()
Public Property success As Boolean
End Class
Public Class Response
Public Property type As String
Public Property tid As Integer
Public Property action As String
Public Property method As String
Public Property result As Result
End Class
But then I am losing the success/failure messages returned by the query
How should I write the class Result in order to colect the properties records As Record(), succes As Boolean and also those objects named "0", "1" and so on...?
Thank you very much for any help.
You have two unrelated problems here:
Your Result
class consists of a set of fixed properties and a set of variable properties with incremented numeric names and a standardized schema for the values. You would like to deserialize the standard properties automatically and capture and deserialize the custom properties as well.
This can be done using JsonExtensionData
. With this attribute, you can temporarily deserialize the custom properties to a Dictionary(of String, JToken)
then later convert to a Dictionary(Of String, Success)
in an [OnDeserialized]
callback. Here Success
is to be a type designed to capture JSON like this:
{
"success": true,
"successes": [ [ "Task successfuly created with S/N #22920" ] ]
}
For documentation, see Deserialize ExtensionData.
Inside the abovementioned Success
type, the "successes"
array contains both arrays of strings and individual strings.
If you define your successes
property as follows:
Public Property successes As List(Of List(Of String))
Then this can be handled using a variation of the SingleOrArrayConverter(Of String)
from How to handle both a single item and an array for the same property using JSON.net, setting it as the item converter via <JsonProperty(ItemConverterType := GetType(SingleOrArrayConverter(Of String)))>
.
Thus your final classes would look like:
Public Class Success
Public Property success As Boolean
<JsonProperty(ItemConverterType := GetType(SingleOrArrayConverter(Of String)))> _
Public Property successes As List(Of List(Of String))
End Class
Public Class Record
Public Property id As Integer
End Class
Public Class Result
Public Property records As Record()
Public Property success As Boolean
<JsonIgnore> _
Public Property successes as Dictionary(Of string, Success)
<JsonExtensionData> _
Private _additionalData as Dictionary(Of string, JToken)
<System.Runtime.Serialization.OnSerializing> _
Sub OnSerializing(ByVal context as System.Runtime.Serialization.StreamingContext)
If successes IsNot Nothing
_additionalData = successes.ToDictionary(Function(p) p.Key, Function(p) JToken.FromObject(p.Value))
Else
_additionalData = Nothing
End If
End Sub
<System.Runtime.Serialization.OnSerialized> _
Sub OnSerialized(ByVal context as System.Runtime.Serialization.StreamingContext)
_additionalData = Nothing
End Sub
<System.Runtime.Serialization.OnDeserializing> _
Sub OnDeserializing(ByVal context as System.Runtime.Serialization.StreamingContext)
_additionalData = Nothing
End Sub
<System.Runtime.Serialization.OnDeserialized>
Sub OnDeserialized(ByVal context as System.Runtime.Serialization.StreamingContext)
If _additionalData IsNot Nothing
successes = _additionalData.ToDictionary(Function(p) p.Key, Function(p) p.Value.ToObject(Of Success)())
End If
_additionalData = Nothing
End Sub
End Class
Public Class Response
Public Property type As String
Public Property tid As Integer
Public Property action As String
Public Property method As String
Public Property result As Result
End Class
Public Class SingleOrArrayConverter(Of T)
Inherits JsonConverter
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
Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
Dim retVal As Object = New List(Of T)()
If reader.TokenType = JsonToken.StartArray Then
serializer.Populate(reader, retVal)
Else
Dim instance As T = DirectCast(serializer.Deserialize(reader, GetType(T)), T)
retVal.Add(instance)
End If
Return retVal
End Function
Public Overrides Function CanConvert(objectType As Type) As Boolean
Return objectType = GetType(List(Of T))
End Function
End Class
Prototype fiddle.