I have a List(Of Object) that I am using in a property of type IEnumerable(Of Object). I can serialize it fine but cannot work out then how to deserialize it from JSON back to a List(Of Object). Any help would be really great please.
My ViewModel:
Public Class ViewModel
Inherits ViewModelBase
Public Class MapSettings
<Display(Name:="Map Name", Description:="Enter a optional name for the map.", GroupName:="Map Settings")>
Public Property MapName As String
<Display(Name:="Map Description", Description:="Enter a optional description for the map.", GroupName:="Map Settings")>
Public Property MapDescription As String
<Display(Name:="Map Comments", Description:="Enter optional comments for the map.", GroupName:="Map Settings")>
Public Property MapComments As String
<Display(Name:="Map Version", Description:="Enter a optional version for the map.", GroupName:="Map Settings")>
Public Property MapVersion As String
End Class
Public Class GeneralSettings
<Display(Name:="Route Colour", Description:="Sets the colour of the routes design line on the map.", GroupName:="General Settings")>
Public Property Foreground As Color
End Class
Private _myItems() As IEnumerable(Of Object)
Public Property MyItems() As IEnumerable(Of Object)
Get
If _myItems Is Nothing Then
Return New List(Of Object)() From {
New MapSettings,
New GeneralSettings With {.Foreground = Colors.Blue}
}
Else
Return _myItems
End If
End Get
Set(value As IEnumerable(Of Object))
_myItems = value
OnPropertyChanged()
End Set
End Property
End Class
My serialize code that I cannot complete:
Dim MyItems_New = JsonConvert.DeserializeObject(Of MyItems???)(MyJsonString)
JSON:
{
"MyItems": [
{
"MapName": null,
"MapDescription": null,
"MapComments": null,
"MapVersion": null
},
{
"Foreground": "#FF0000FF"
}
]
}
I just found that Newtonsoft has built-in support for type handling, which can be enabled by setting the JsonSerializerSettings.TypeNameHandling property and passing it to the serialization methods. As long as you control the input, this should let you both serialize and deserialize your list without a problem.
Serialize:
Dim myItems = JsonConvert.SerializeObject(myVM.MyItems, Formatting.None, New JsonSerializerSettings() With { .TypeNameHandling = TypeNameHandling.Auto })
Deserialize:
Dim myItems_New = JsonConvert.DeserializeObject(Of List(Of Object))(MyJsonString, New JsonSerializerSettings() With { .TypeNameHandling = TypeNameHandling.Auto })
In your question, MyJsonString appears to be a serialized version of your ViewModel class rather than just the list itself. If this is the case, change DeserializeObject(Of List(Of Object)) to DeserializeObject(Of ViewModel).
Resulting JSON:
[
{
"$type": "MapSettings, YourProjectNamespace",
"MapName": "New York",
"MapDescription": "Map over New York",
"MapComments": null,
"MapVersion": "v1"
},
{
"$type": "GeneralSettings, YourProjectNamespace",
"Foreground": "#FF0000"
}
]
Try it online (C#):
https://dotnetfiddle.net/0jCIGL
However, if these two classes are all you are planning to use this list for, then you'd be better off using something along the lines of Jimi's proposed solution, as then you're always working with strongly-typed objects.
Related
In my program, it's the first time that I'm trying to parse JSON content.
The result I'm trying to get is a DataTable with columns like this:
| Text of group | Text of Item | Command of Item |
The JSON is the following:
{
"Groups": [
{
"Items": [
{
"Command": "Framework.Windows.Components.ESScrollerForm, ESGrid|SHOW|Χρήστες|ESGOUser|ESGOUser_def|||65535",
"Key": "834888dd-c4d5-449a-96b7-67db5c3d2692",
"Text": "Users",
"ImageIndex": -1
},
{
"Command": "Framework.Windows.Components.ESScrollerForm, ESGrid|SHOW|QuestionaireSurveyorQuery|ESTMTask|QuestionaireSurveyorQuery|||0",
"Key": "b71de66d-2baf-4452-ada7-8fc67044876b",
"Text": "QuestionaireSurveyorQuery"
}
],
"Expanded": true,
"Tag": "",
"Key": "b741e67a-a3cd-4b97-91cf-ae9c9d9db7d7",
"Text": "Settings",
"ImageIndex": -1
},
{
"Items": [
{
"64String": "Soap",
"Command": "cInvoke|Booked Requests Agent Booking|SHOW|ESMIS|BookedReqAgentBook||False",
"Key": "bfbc3d4a-ef8a-49a0-918a-331813ba90fb",
"Text": "Requests Agent Booking",
"ImageIndex": -1
},
{
"64String": "Jrse",
"Command": "cInvoke|HHG SF Profit \u0026 Loss|SHOW|ESMIS|HHGFileProfitability||False",
"Key": "cf1cbffc-aba9-4e0f-8d6c-ba7219932fb6",
"Text": "HHG SF Profit \u0026\u0026 Loss",
"ImageIndex": -1
}
],
"Tag": "..CSShortcuts\\HHGReporting.ebl",
"Key": "eff0d713-a70e-4582-a103-b8cc5cecdad6",
"Text": "HHGReporting",
"ImageIndex": -1
}
]
}
In the past, I have succesfully parsed complex XML using XPATH, but now I have struggled a lot using Newtonsoft.Json with no success.
What I have tried, is to first create 3 classes using online generators:
Public Class Rootobject
Public Property Groups() As Group
End Class
Public Class Group
Public Property Items() As Item
Public Property Expanded As Boolean
Public Property Tag As String
Public Property Key As String
Public Property Text As String
Public Property ImageIndex As Integer
End Class
Public Class Item
Public Property Command As String
Public Property Key As String
Public Property Text As String
Public Property ImageIndex As Integer
Public Property 64String As String
End Class
Any ideas how the deserialized commands should be, in order the get the data as a DataTable with the structure described?
You have an almost working class model, some changes are necessary to make it work as intended.
This kind of syntax is misleading:
Public Property Groups() As Group
This is not a collection of objects, it's just a single object of Type Group.
Change all to:
Public Property Groups As Group()
'or
Public Property Groups As List(Of Group)
To convert to DataTable with a specific selection of Columns, you need to iterate the Groups collection and, for each group, iterate the Items collection, to extract the values you need.
Here, I'm using a specialized class, GroupsHandler, that contains the class Model used to deserialize a compatible JSON and to convert to DataTable partial content of the resulting data structure.
The GroupsHandler class exposes two Public methods:
Deserialize(), used to convert to .Net classes the JSON content
ToDataTable(), used to create a DataTable from the deserialized content.
You can initialize the handler and call these method as:
Dim handler = New GroupsHandler(Json)
' Only deserialize
Dim myGroups = handler.Deserialize()
' Deserialize and convert to DataTable
Dim dt = handler.ToDataTable()
The Group class name is changed in ItemsGroup, since Group is a language keyword.
The 64String property name changed in String64, since you cannot have a Property Name that begins with a number.
Imports Newtonsoft.Json
Public Class GroupsHandler
Private root As GroupsRoot = Nothing
Private m_json As String = String.Empty
Public Sub New(json As String)
m_json = json
End Sub
Public Function Deserialize() As List(Of ItemsGroup)
root = JsonConvert.DeserializeObject(Of GroupsRoot)(m_json)
Return root.Groups
End Function
Public Function ToDataTable() As DataTable
If root Is Nothing Then
If String.IsNullOrEmpty(m_json) Then Return Nothing
Deserialize()
End If
Dim dt As New DataTable("Groups")
dt.Columns.AddRange(New DataColumn() {
New DataColumn("GroupText", GetType(String)),
New DataColumn("ItemText", GetType(String)),
New DataColumn("ItemCommand", GetType(String))
})
For Each grp In root.Groups
For Each item In grp.Items
dt.Rows.Add(New Object() {grp.Text, item.Text, item.Command})
Next
Next
Return dt
End Function
Public Class GroupsRoot
Public Property Groups As List(Of ItemsGroup)
End Class
Public Class ItemsGroup
Public Property Items As List(Of Item)
<JsonProperty("Expanded", NullValueHandling:=NullValueHandling.Ignore)>
Public Property Expanded As Boolean?
Public Property Tag As String
Public Property Key As Guid
Public Property Text As String
Public Property ImageIndex As Long
End Class
Public Class Item
Public Property Command As String
Public Property Key As Guid
Public Property Text As String
<JsonProperty("ImageIndex", NullValueHandling:=NullValueHandling.Ignore)>
Public Property ImageIndex As Long?
<JsonProperty("64String", NullValueHandling:=NullValueHandling.Ignore)>
Public Property String64 As String
End Class
End Class
I have been searching the web back an forth but couldn't find a hint to my issue.
I'm calling a REST API via RestSharp Client. I retrieve a response like this:
{
"meta": {
"query_time": 0.007360045,
"pagination": {
"offset": 1,
"limit": 100,
"total": 1
},
"powered_by": "device-api",
"trace_id": "a0d33897-5f6e-4799-bda9-c7a9b5368db7"
},
"resources": [
"1363bd6422274abe84826dabf20cb6cd"
],
"errors": []
}
I want to query the value of resources at the moment. This is the code I use:
Dim id_request = New RestRequest("/devices/queries/devices/v1?filter=" + filter, Method.GET)
id_request.AddHeader("Accept", "application/json")
id_request.AddHeader("Authorization", "bearer " + bearer)
Dim data_response = data_client.Execute(id_request)
Dim data_response_raw As String = data_response.Content
Dim raw_id As JObject = JObject.Parse(data_response_raw)
Dim id = raw_id.GetValue("resources").ToString
Unfortunately, I'm only getting ["1363bd6422274abe84826dabf20cb6cd"] as a reply instead of 1363bd6422274abe84826dabf20cb6cd
Can anyone point me into the right direction?
I have also tried to deserialize using JsonConvert.DeserializeObject() but I somehow fail.
I found this solution here but if I try to rebuild it fails as it doesn't recognize the Dictionary part
Dim tokenJson = JsonConvert.SerializeObject(tokenJsonString)
Dim jsonResult = JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(jsonString)
Dim firstItem = jsonResult.Item("data").Item(0)
EDIT:
When trying to deserialize the root as suggested but seems as if the 2nd response is nested JSON.
I have a reply like:
dr = {
"meta": {
"query_time": 0.004813129,
"powered_by": "device-api",
"trace_id": "5a355c86-37f7-416d-96c4-0c8796c940fc"
},
"resources": [
{
"device_id": "1363bd6422274abe84826dabf20cb6cd",
"policies": [
{
"policy_type": "prevention",
"policy_id": "1d34205a4e2c4d1991431c037c8e5734",
"applied": true,
"settings_hash": "7cb00a74",
"assigned_date": "2021-02-22T13:56:37.759459481Z",
"applied_date": "2021-02-22T13:57:19.962692301Z",
"rule_groups": []
}
],
"meta": {
"version": "352"
}
}
],
"errors": []
}
and I tried:
Dim restApiResponse = JsonConvert.DeserializeObject(Of RestApiResponseRoot)(dr)
' This is your array of strings
Dim resources = restApiResponse.Resources
Unfortunately I get
Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: {. Path 'resources', line 8, position 3.'
The resources Property is an array. As usual, you need to specify which element of the array you want to consider. In this case, the first one, i.e., the element at index 0.
Dim jsonObject = JObject.Parse(data_response_raw)
Dim firstResource = jsonObject("resources")(0).ToString()
If you instead want the array content as a String array, not just the first element - assuming resources could contain more than one string (it's an array after all) - deserialize to String():
Dim jsonObject = JObject.Parse(data_response_raw)
Dim resources = JsonConvert.DeserializeObject(Of String())(jsonObject("resources").ToString())
In case you need the whole JSON response, I suggest to deserialize to a class Model that represents the JSON:
Public Class RestApiResponseRoot
Public Property Meta As Meta
Public Property Resources As List(Of String)
Public Property Errors As List(Of Object)
End Class
Public Class Meta
<JsonProperty("query_time")>
Public Property QueryTime As Double
Public Property Pagination As Pagination
<JsonProperty("powered_by")>
Public Property PoweredBy As String
<JsonProperty("trace_id")>
Public Property TraceId As Guid
End Class
Public Class Pagination
Public Property Offset As Long
Public Property Limit As Long
Public Property Total As Long
End Class
You can then deserialize the Model's Root object - the class named RestApiResponseRoot here - and access its Properties as usual:
Dim restApiResponse = JsonConvert.DeserializeObject(Of RestApiResponseRoot)(
data_response_raw
)
' This is your array of strings
Dim resources = restApiResponse.Resources
The other JSON response is slightly different, the Response Property contains an array of objects instead of string.
Some more properties and nested object are added. You just need to adjust the Model.
Public Class RestApiResponseRoot2
Public Property Meta As RootObjectMeta
Public Property Resources As List(Of Resource)
Public Property Errors As List(Of Object)
End Class
Public Class RootObjectMeta
<JsonProperty("query_time")>
Public Property QueryTime As Double
<JsonProperty("powered_by")>
Public Property PoweredBy As String
<JsonProperty("trace_id")>
Public Property TraceId As Guid
End Class
Public Class Resource
<JsonProperty("device_id")>
Public Property DeviceId As String
Public Property Policies As List(Of Policy)
Public Property Meta As ResourceMeta
End Class
Public Class ResourceMeta
Public Property Version As String
End Class
Public Class Policy
<JsonProperty("policy_type")>
Public Property PolicyType As String
<JsonProperty("policy_id")>
Public Property PolicyId As String
Public Property Applied As Boolean
<JsonProperty("settings_hash")>
Public Property SettingsHash As String
<JsonProperty("assigned_date")>
Public Property AssignedDate As DateTimeOffset
<JsonProperty("applied_date")>
Public Property AppliedDate As DateTimeOffset
<JsonProperty("rule_groups")>
Public Property RuleGroups As List(Of Object)
End Class
Dim restApiResponse2 = JsonConvert.DeserializeObject(Of RestApiResponseRoot2)(dr)
Dim resources As List(Of Resource) = restApiResponse2.Resources
' DeviceId of the first Resources object
Dim deviceId = resources(0).DeviceId
You can use some on-line resources to handle your JSON objects:
JSON Formatter & Validator
QuickType - JSON to .Net classes - C#, no VB.Net
JSON Utils - JSON to .Net classes - includes VB.Net. Somewhat less capable than QuickType.
Try trimming the resources value with " character in first and last of the output
I have a rest API which returns me a json response which is an array of docs which in turn has multiple arrays it self. Below is a sample of a doc i like to deserialize
{
"DocId": "contact::6f128681-218d-409d-b71d-31031852057d",
"Name": "Joe F Miller",
"buckets": [
{
"DocId": "leadbucket::5652A756-5B58-45A5-9566-9C85E8783440",
"text": "Dead Leads"
},
{
"DocId": "leadbucket::8A234FC1-6389-485D-8BDE-7FCB1E7639E0",
"text": "No Follow-Up Needed"
},
{
"DocId": "leadbucket::C97632BE-5A24-4AE7-8D18-4DFE174F0D0F",
"text": "Long-Term Buyers"
},
{
"DocId": "leadbucket::D6802064-8AC5-4E5A-855E-B59C32859C81",
"text": "New Lead"
}
],
"emails": [
{
"other": "demo#test.com"
},
{
"work": "work#demo.com"
},
{
"work": "work2#demo.com"
}
],
"followup": {
"date": "05/01/2019",
"type": "phone"
},
"lastactivity": "04/05/2019",
"phones": [
{
"home": "(213) 444-2222"
},
{
"work": "(949) 555-1212"
}
],
"tags": [
{
"DocId": "tag::FC276FBD-DC3A-4E18-8244-E89EF24E022E",
"text": "Buyer"
},
{
"DocId": "tag::EA5DE0FB-34B0-4F7C-B631-177D6BD2F65E",
"text": "Investor"
}
]
}
After i get the response from my API i use the below code to try to convert the data.
ContactList = JsonConvert.DeserializeObject(Of List(Of Contact.ContactList))(read.Item("Data").ToString)
So i hope someone can point me in the right direction on how to create a class to store this type of doc and how to Deserialize it. Do i have to do this in my class or do i have to call a Deserialize for each array in the Json ?
Another issue i have is the emails and phones section, as the key value is not unique and i dont always know what will get returned. how would i build a class to store this as the key field can change.
An example, using an IEnumerable(Of Dictionary(Of String, String)) to deserialize JSON Objects that have an undefined number of elements, with an undefined combination of (Key, Value) pairs per object.
The Key part may also repeat, as shown in the exmple:
"emails": [
{"other": "demo#test.com"},
{"work": "work#demo.com"},
{"work": "work2#demo.com"}
The other, work keys may be different each time and may repeat.
Deserializing/Serializing this property with a IEnumerable(Of Dictionary(Of String, String)), allows to
The sample class object, Contacts, contains the methods (simplified, here) that perform the deserialization of the JSON Object and the seriaization of the Object Root, reproducing the original JSON Object.
These methods are static (Shared), you just need to call the method, no need to create an instance of the Contacts class.
For example, deserialize a JSON Object (a string received from a service):
Dim myContacts As Contacts.RootObject = Contacts.DeserializeJSON(JSONObject)
Serialize the class object back to the original string:
Dim myJsonContacts = Contacts.SerializeJSON(myContacts)
Compare the JSON just serialized to the original and to see whether they match.
List all the Emails and Phone Numbers in the deserialized class object:
(just an example: converting to List is not necessary)
myContacts.Emails.ToList().ForEach(
Sub(em) Console.WriteLine($"from: {em.Keys(0)}, Email: {em.Values(0)}"))
myContacts.Phones.ToList().ForEach(
Sub(ph) Console.WriteLine($"place: {ph.Keys(0)}, Phone Number: {ph.Values(0)}"))
Access a single Email (or Phone Number):
(any other method to extract the (Key, Value) pair from a Dictionary can of course be used)
Dim eMailFrom = myContacts.Emails(0).Keys(0)
Dim eMailValue = myContacts.Emails(0).Values(0)
Get all the Emails from a specific location (work, here):
Dim emailFromWork = myContacts.Emails.Where(Function(em) em.Keys.Contains("work"))
The Contacts class:
Some properties are decorated with a <JsonProperty()>. The property name is changed because the original JSON uses names that are reserved keywords in the language.
For example:
<JsonProperty("text")>
Public Property BucketText As String
Imports Newtonsoft.Json
Public Class Contacts
Public Shared Function DeserializeJSON(JsonObject As String) As RootObject
Return JsonConvert.DeserializeObject(Of RootObject)(JsonObject)
End Function
Public Shared Function SerializeJSON(classObject As RootObject) As String
Return JsonConvert.SerializeObject(classObject)
End Function
Partial Public Class RootObject
Public Property DocId As String
Public Property Name As String
Public Property Buckets As List(Of Bucket)
Public Property Emails As Dictionary(Of String, String)()
Public Property Followup As Followup
Public Property Lastactivity As String
Public Property Phones As IEnumerable(Of Dictionary(Of String, String))
Public Property Tags As List(Of Tag)
End Class
Partial Public Class Bucket
Public Property DocId As String
<JsonProperty("text")>
Public Property BucketText As String
End Class
Partial Public Class Tag
Public Property DocId As String
<JsonProperty("text")>
Public Property TagText As String
End Class
Partial Public Class Followup
<JsonProperty("date")>
Public Property FollowupDate As String
<JsonProperty("type")>
Public Property FollowupType As String
End Class
End Class
EDITED:
I got stuck while getting value of a JSON object in vb.net. My JSON request posts data like given below:
function submitEmail() {
var ClientsPersonalInfo = {
FullName: $("#FullName").val(),
PhoneNumber: $("#PhoneNumber").val(),
EmailAddress: $("#EmailAddress").val(),
DOB: $("#DOB").val(),
Occupation: $("#Occupation").val(),
NINumber: $("#NINumber").val(),
FullAddress: $("#FullAddress").val()
}
var ClientsData = {};
ClientsData.ClientsPersonalInfo = ClientsPersonalInfo;
var d = '{"ClientsData":' + JSON.stringify(ClientsData) + '}'
$.ajax({
type: "POST",
url: "add-new-client.aspx/SubmitEmail",
data: d,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
alert(response)
},
failure: function (msg) {
alert(msg);
}
});
}
JSON Object Looks Like
{
"ClientsPersonalInfo": {
"FullName": "",
"PhoneNumber": "",
"EmailAddress": "",
"DOB": "",
"Occupation": "",
"NINumber": "",
"FullAddress": ""
}
}
The above request returns an object in vb.net
VB Code:
<WebMethod()> _
Public Shared Function SubmitEmail(ByVal ClientsPersonalInfo As Object) As String
// What to do next to get object "ClientsPersonalInfo"
// I want to access properties of the object like
//Dim name As String = ClientsPersonalInfo.FullName
Return "Successfully Converted."
End Function
No I want to get values of this object and needs to append in a table. Please guide me how to get values of the above object?
First make sure your Json is in valid format using jsonlint
Then generate class base on it using jsonutils
Public Class ClientsPersonalInfo
Public Property FullName As String
Public Property PhoneNumber As String
Public Property EmailAddress As String
Public Property DOB As String
Public Property Occupation As String
Public Property NINumber As String
Public Property FullAddress As String
End Class
Public Class ClientsVehicleInfo
Public Property DrivingLicense As String
Public Property VehicleMakeModel As String
Public Property VehicleColour As String
Public Property PolicyNumber As String
Public Property TypeOfCover As String
Public Property VehicleStoredIn As String
End Class
Public Class ClientsData
Public Property ClientsPersonalInfo As ClientsPersonalInfo
Public Property ClientsVehicleInfo As ClientsVehicleInfo
End Class
Public Class ClientData
Public Property ClientsData As ClientsData
End Class
Use Newtonsoft JSON to deserialize your Json into object(s) then you may simply access its properties value. (remember to add Json.net to your project using Manage NuGet Packages)
Imports Newtonsoft.Json
Dim obj = JsonConvert.DeserializeObject(Of Dictionary(Of String, ClientsData))(yourJsonString)
At least one problem is not using Option Strict On. The code at fault:
Shared Function SubmitEmail(ByVal ClientData As Object) As String
Dim obj = JsonConvert.DeserializeObject(Of NewClientData)(ClientData)
If you turn on Option Strict that will not compile because JsonConvert.DeserializeObject takes a string argument. I am not sure why the exception (image now removed) seems to come from VB rather than Newtonsoft, but that isnt helping.
Your deserialized object will also just disappear when it goes out of scope when the method ends.
Applicable to Edit #9
The error mentioning a Dictionary seems misleading and probably something internal relating to how the properties are collected (many times json can be deserialized to a Dictionary(Of String, String). Given the json posted (with data):
{
"ClientsData": {
"ClientsPersonalInfo": {
"FullName": "Ziggy Le Strange",
"PhoneNumber": "505050",
"EmailAddress": "ziggy#foobar.com",
"DOB": "",
"Occupation": "Freelancer",
"NINumber": "7",
"FullAddress": "123 Easy street"
}
}
}
There are actually 3 classes: ClientsPersonalInfo with the data, ClientsData which is a class containing that one and in previous edits also included a ClientsVehicleInfo class.
But there is yet another class represented by the enclosing {...}. The robots who can create the classes for you name it Example or RootObject. In this case, I would call it ClientContainer.
This works:
' the outermost {}
Public Class ClientContainer
Public Property ClientsData As ClientsData
End Class
Public Class ClientsPersonalInfo
Public Property FullName As String
Public Property PhoneNumber As String
Public Property EmailAddress As String
Public Property DOB As String
Public Property Occupation As String
Public Property NINumber As String
Public Property FullAddress As String
End Class
Public Class ClientsData
Public Property ClientsPersonalInfo As ClientsPersonalInfo
Public Property ClientsVehicleInfo As ClientsVehicleInfo
End Class
Public Class ClientsVehicleInfo
' whatever it is supposed to hold
End Class
To deserialize the data (you may have to adapt it for web use, Shared seems incorrect to me):
' pass in the json AS STRING
' returns JUST the ClientsPersonalInfo
Public Function GetClientData(jsonData As String) As ClientsPersonalInfo
' you must use the container class
Dim client = JsonConvert.DeserializeObject(Of ClientContainer)(jsonData )
' TEST:
Console.WriteLine(client.ClientsData.ClientsPersonalInfo.FullName)
Return client.ClientsData.ClientsPersonalInfo
End Function
ClientsData seems to be an unneeded layer. The container could hold both of the other objects directly. If this is meant to hold info for more than one client, you would have keys in place of "ClientsData": in the json (e.g. "ziggy":{}, "zacky":{}, "zoey":{}.
Output:
Ziggy Le Strange
Since, as per comment, that vehicle info is part of the deal, you can change it to return ClientsData which holds both the Personal and Vehicle info:
Public Function GetClientData(jsonData As String) As ClientsData
' you must use the container class
Dim client = JsonConvert.DeserializeObject(Of ClientContainer)(jsonData )
Return client.ClientsData
Turn on Option Strict
Dont box parameters or returns As Object, they loose some of their meaning.
Keep in mind that the outermost braces in json represent a container object
Also, storing a Date as string looks bad too.
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 what I'm seeing with the json
One way it comes in ...
{
"Make": "Dodge",
"Model": "Charger",
"Lines": [
{
"line": "base",
"engine": "v6",
"color": "red"
},
{
"line": "R/T",
"engine": "v8",
"color": "black"
}
],
"Year": "2013"
}
Another way it could come in
{
"Make": "Dodge",
"Model": "Charger",
"Lines": {
"line": "base",
"engine": "v6",
"color": "red"
},
"Year": "2013"
}
Here is what I've been using for code which works on the first way and throws an exception in the second case. Been scouring the web for ways to implement this and am really stuck.
Public Class jsonCar
Public Property make As String
Public Property model As String
Public Property lines 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 car As jsonCar = JsonConvert.DeserializeObject(Of jsonCar)(json)
Console.WriteLine("Make: " & car.make)
Console.WriteLine("Model: " & car.model)
Console.WriteLine("Year: " & car.year)
Console.WriteLine("Lines: ")
For Each ln As jsonCarLines In car.lines
Console.WriteLine(" Name: " & ln.line)
Console.WriteLine(" Engine: " & ln.engine)
Console.WriteLine(" Color: " & ln.color)
Console.WriteLine()
Next
Console.ReadLine()
End Sub
End Module
I'm guessing this will likely need a custom JsonConverter, but I'm a bit at a loss as to how to set that up.
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
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
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.
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