Deserialize nested JSON and VB.Net - json

I try to get the values from the basic section in the following json result
{
"responseCode": "Ok",
"responseMessage": "",
"ssnStatus": "Active",
"basic": {
"firstName": "Testnamn",
"givenName": "Gettnamn",
"surName": "Testname",
"middleName": null,
"lastName": "Lastname",
"co": null,
"street": "Teststreet lgh 1202",
"zipCode": "92609",
"city": "Stad"
},
"phoneNumbers": {
"phoneNumbers": []
},
"ssnStatusBlock": null
}
I can get the first level (ssnStatus) with the code below, but how do I get the firstName, givenName etc?
Dim post As Post = JsonConvert.DeserializeObject(Of Post)(exampleJson)
Dim ssnStatus As String = post.ssnStatus
and
Public Class Post
Public Property ssnStatus As String
End Class

You are missing the properties and classes definition for all the other membersof the JSON object.
Create a new class file in your Project. Give it a name that properly describe the JSON usage,
add the Imports Newtonsoft.Json import,
copy a sample of the JSON object that describes the JSON structure (the one you have here is good),
position the caret inside the new class definition,
see the Visual Studio menu: Edit -> Paste Special -> Paste JSON as classes
Visual Studio will create all the classes and properties needed to handle the JSON object you selected.
Note: With more complex classes, it may happen that the result Visual Studio produces is not exactly what you require. In this case, try one of the specialized WebSites that provide a free conversion service:
Json Utils (VB.Net, C#, Java, Javascript, more...)
QuickType (C#, C++, Java, Javascript, Python, Go, more...)
json2csharp (C#)
JSON Formatter (JSON formatting and validation)
The new root class definition will be named Rootobject. Change this name as needed,
to make it more clear what the class is used for.
This is the class definition that Visual Studio creates with the JSON object in your question.
I created a class Project class named MyWebSitePost, created the JSON bject class definition as previously described, I then renamed the default master class Post, replacing the default Rootobject name:
Public Class MyWebSitePost
Public Class Post
Public Property responseCode As String
Public Property responseMessage As String
Public Property ssnStatus As String
Public Property basic As Basic
Public Property phoneNumbers As Phonenumbers
Public Property ssnStatusBlock As Object
End Class
Public Class Basic
Public Property firstName As String
Public Property givenName As String
Public Property surName As String
Public Property middleName As Object
Public Property lastName As String
Public Property co As Object
Public Property street As String
Public Property zipCode As String
Public Property city As String
End Class
Public Class Phonenumbers
Public Property phoneNumbers() As Object
End Class
End Class
You can then use the code you already have to access all the other properties:
(Some Properties type may have been set to Object; modify as
required).
Dim JsonPost As Post = JsonConvert.DeserializeObject(Of Post)(exampleJson)
Dim ssnStatus As String = JsonPost.ssnStatus
Dim FirstName As String = JsonPost.basic.firstName
and so on.
Note:
As you can see, all properties have the default name, as defined in the JSON object. Some properties names are not really adeguate to describe thier content. For example the co property is probably the Country name. To change the property name, you can use the <JsonProperty> attribute, which references the JSON object original name and use a custom name for the Property:
'(...)
<JsonProperty("co")>
Public Property Country As Object
'(...)
You can then access this Property using the custom name:
Dim CountryName As String = JsonPost.basic.Country

Related

How to read json return string?

Having trouble to read json return in a useful way
Searching "all over" to find how to create classes and how to de-serialize json return
This is the json return:
[[{"metadata":{},"contentType":0,"contentId":0,"objectName":"Mi","objectId":"1","classId":"118"},
{"metadata":{},"contentType":0,"contentId":0,"objectName":"BA","objectId":"224445","classId":"103"},
{"metadata":{},"contentType":0,"contentId":0,"objectName":"1","objectId":"239011","classId":"104"},
{"metadata":{},"contentType":0,"contentId":0,"objectName":"1","objectId":"239309","classId":"105"}]]
Tried Visual Studio (VB.net) Paste Special to create json classes, but I can't seem to get my head around how to use it. Using Newtonsoft.Json.
These are the classes, how do I deserialize json and make it useful?
Public Class Rootobject
Public Property Property1()() As Class1
End Class
Public Class Class1
Public Property metadata As Metadata
Public Property contentType As Integer
Public Property contentId As Integer
Public Property objectName As String
Public Property objectId As String
Public Property classId As String
End Class
Public Class Metadata
End Class
Your JSON is an array of arrays of Class1 objects, so you need to deserialize to that.
Dim data As Class1()() = JsonConvert.DeserializeObject(Of Class1()())(json)
You don't actually need the Rootobject class here.
Working demo: https://dotnetfiddle.net/1JuaYk

vb.net Serializing Collections unable to add a collection to object?

I'm trying to generate some JSON that looks like this:
{
"#type": "MessageCard",
"sections": [
{
"activityTitle": " Request",
"facts": [
{
"name": "name1",
"value": "Value"
},
{
"name": " Date:",
"value": "Value Date"
}
],
"text": "Some Test."
}
],
"potentialAction": [
{
"#type": "ActionCard",
"name": "Add a comment",
"inputs": [
{
"#type": "TextInput",
"id": "comment",
"isMultiline": true
}
]
}
]
}
I performed a paste special into VS and it generated the class structure for me as such:
Public Class MessageCard
Public Property type As String
Public Property context As String
Public Property summary As String
Public Property themeColor As String
Public Property sections() As Section
Public Property potentialAction() As Potentialaction
End Class
I'm trying to add the sections to the object as such:
Dim m as New MessageCard
Dim s As New List(Of Section)
s.Add(s1)
s.Add(s2)
m.sections = s
The compiler complains that it cannot convert a list of Sections into a Section. Did the class get generated incorrectly, or am i constructing it incorrectly?
First, your JSON is not quite complete and the Classes you show wont create that JSON.
As posted, that JSON simply shows a Sections and potentialAction class which are not related in any way. An enclosing [ ... ] is needed to represent the MessageCard class containing the two of them.
[{
"#type": "MessageCard",
...
}]
Next, the class you have shows all sorts of things not present in the JSON: context, summary and themeColor for instance. I assume those might be missing for brevity, but it is confusing. There is also 2 other Types missing which are in the JSON, Fact and Input.
Corrected, the classes should be:
Public Class MsgCard
<JsonProperty("#type")>
Public Property ItemType As String
Public Property sections As List(Of Section)
Public Property potentialAction As List(Of Potentialaction)
Public Sub New()
sections = New List(Of Section)
potentialAction = New List(Of Potentialaction)
End Sub
End Class
Public Class Section
Public Property activityTitle As String
Public Property facts As Fact()
Public Property text As String
End Class
Public Class Fact
Public Property name As String
Public Property value As String
End Class
Public Class Potentialaction
<JsonProperty("#type")>
Public Property ActionType As String
Public Property name As String
Public Property inputs As Input()
End Class
Public Class Input
<JsonProperty("#type")>
Public Property InputType As String
Public Property id As String
Public Property isMultiline As Boolean
End Class
Notes
You did not specify a JSON serializer, so this is prepared to JSON.NET as recommended by Microsoft.
#type is an illegal property name, so the JsonProperty attribute is used to create an alias. I also used less confusingly redundant names.
You may want to change Fact and Input to List(Of T) if you will be creating and pushing them into the class object as well.
Finally, for the actual question you asked, most of the automatic class generators have trouble with arrays (even VS).
Public Property sections() As Section
' should be:
Public Property sections As Section()
That simply declares that sections will be an array, it does not create the array. Normally this is not a problem because the Serializer/Deserializer will create the array. To allow code external to the class to add to them, you probably want to use a List as the classes above do, then create the instance in the constructor:
Public Sub New()
sections = New List(Of Section)
potentialAction = New List(Of Potentialaction)
End Sub

JSON Format Deserialize Error

I am new to manipulating JSON format data retrieved through an Web API. I'm using Newtonsoft.Json.JsonConvert to deserialize and assign to datatable.
This is the JSON data I am trying to get into a datatable.
[{
"classDesc":"SIDEWALK,DRIVEWAY,CURB",
"classCode":"EH",
"legend":"017",
"isActive":"Y",
"atrSpaCityDistrictId":"00D17209F8F25F6D4A00011302",
"atrSpaCitieDistrict":{
"cityDistrict":"",
"isActive":"1",
"atrSpaClassLegends":null,
"id":"00D17209F8F25F6D4A00011302"
},
"id":"00D1748B8DA0AB0A7400011202"
}]
I have created the below classes
Public Class RootObject
Public Property classDesc As String
Public Property classCode As String
Public Property id As Integer
Public Property legend As Integer
Public Property isActive As String
Public Property atrSpaCityDistrictId As Integer
Public Property atrSpaCitieDistrict As List(Of Result) End Class
Public Class Result
Public Property cityDistrict As String
Public Property isActive As Integer
Public Property atrSpaClassLegends As List(Of Legend)
Public Property id As Integer
End Class
Public Class Legend
End Class
Below is my VB.Net code:
Dim table As DataTable Dim client As New HttpClient() client.BaseAddress = New Uri("http://localhost:5000/") client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
Dim response As HttpResponseMessage = client.GetAsync("api/spaclasslegend").Result
If response.IsSuccessStatusCode Then
Dim json As String = response.Content.ReadAsStringAsync().Result table = JsonConvert.DeserializeObject(Of DataTable)(json)
End If
Getting below error
"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'Alco.APTIS.Services.RootObject' because the type requires a JSON
object (e.g. {"name":"value"}) to deserialize correctly."
I have tried different ways to fix this using stackoverflow.com and finally posting here for help.
You have two unrelated issues.
Firstly, your root object doesn't match your JSON. If I copy your JSON into a code generator such as http://jsonutils.com/, I get the following, using a List(Of Legend) for the null atrSpaClassLegends property:
Public Class AtrSpaCitieDistrict
Public Property cityDistrict As String
Public Property isActive As String
Public Property atrSpaClassLegends As List(Of Legend)
Public Property id As String
End Class
Public Class RootObject
Public Property classDesc As String
Public Property classCode As String
Public Property legend As String
Public Property isActive As String
Public Property atrSpaCityDistrictId As String
Public Property atrSpaCitieDistrict As AtrSpaCitieDistrict
Public Property id As String
End Class
Public Class Legend
End Class
Having done this, it is now possible to deserialize your JSON into a List(Of RootObject):
Dim root = JsonConvert.DeserializeObject(Of List(Of RootObject))(jsonString)
Note you must deserialize as a List(Of RootObject) (or similar .Net collection such as an array) since your top level JSON container is a JSON array -- an ordered comma-separated sequence of values surrounded by [ and ]. This is explained in the Json.NET Serialization Guide. Sample fiddle.
Secondly, despite having created these types, in the code shown in your question you are not using them at all! Instead, you are deserializing to a DataTable. And unfortunately the atrSpaCitieDistrict property is a nested object:
{
"atrSpaCitieDistrict": {
"cityDistrict": "",
"isActive": "1",
"atrSpaClassLegends": null,
"id": "00D17209F8F25F6D4A00011302"
},
}
Json.NET does not support automatically deserializing column values that are complex objects into untyped data tables, and will throw an exception if one is encountered. Instead, you would need to deserialize into a typed data table. However, this may be a typo in your question; the exception error message indicates you are actually trying to deserialize into an object of type Alco.APTIS.Services.RootObject and not a DataTable.

convert object json to object vb .net

Hello I need to convert this objetct to vb.
{"user":
[
{"name":"CompanyName"},
{"password":"CompanyPassword"},
{"email":"mail#company.com"},
{"name":"UserName"},
{"email":"user#mail.com"}
]
}
I try with this:
Public Class InfoObjUser
Public Property name As String
Public Property password As String
Public Property email As String
End Class
Public Class ObjUser
Public Property user As New List(Of InfoObjUser)
End Class
but when I go to serialize json object created in vb
I see that there are no curly brackets.
Also in vb I can't add {"name":"UserName"},{"email":"user#mail.com"}
because they are already present.
Take a look at this: JSON Serialization in .Net.
You need to add the System.Runtime.Serialization as reference to your project, and then set up a serializer object in your code. You also need to tag, which fields in your class match your JSON code.
This would be your class setup:
<DataContract>
Public Class InfoObjUser
<DataMember(Name:="Name")>
Public Property name As String
<DataMember(Name:="Password")>
Public Property password As String
<DataMember(Name:="Email")>
Public Property email As String
End Class
<DataContract>
Public Class ObjUser
<DataMember>
Public Property user As New List(Of InfoObjUser)
End Class
And a Json reader class for that would look like this:
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Json
Public Class JsonReader
Public Shared Function FromString(Of T)(ByVal input As String) As T
Dim serializer As New DataContractJsonSerializer(GetType(T))
Dim memStream As New MemoryStream()
Dim sw As New StreamWriter(memStream)
sw.Write(input)
sw.Flush()
memStream.Position = 0
Dim returnObj As T = CType(serializer.ReadObject(memStream), T)
Return returnObj
End Function
End Class
And finally, you can call this class like this:
Dim jsonString ' = ...Input string here
Dim user As ObjUser = JsonReader.FromString(Of ObjUser)(jsonString)
However, it is not possible to have the name for two different data objects, as the serializer would not know where to map them to, as it goes by Object name.
I don't know how established this data structure already is, but if it's possible, I would suggest you to change the duplicated names, as no real mapping can occur when doing so programmatically.
<DataMember(Name:="UserName")>
Public Property username As String
<DataMember(Name:="UserEmail")>
Public Property usermail As String
If you would add something like this to your data model class and rename the field names in your Json, then it would work.

ArgumentOutOfRange exception when deserializing to a DataSet with Json.Net

I have the following JSON string in a variable called strJSON.
{
"results":[
{
"templateName":"HUD Section 8",
"userID":"2",
"mobileObjectId":"4582",
"source":"M",
"inspectionType":"A",
"notes":"Window in bedroom needs repair.",
"agencyID":"",
"requestDate":"2014-05-09 00:00:00",
"agencyName":"",
"inspectionTimeBegun":"2014-05-09 14:00:17",
"inspectionDate":"2014-05-09 14:30:00",
"inspectionID":135,
"inspectionTimeComplete":"2014-05-09 14:29:25",
"summaryDecision":"F",
"createdAt":"2014-05-09T18:29:35.050Z",
"updatedAt":"2014-05-09T18:29:35.050Z",
"objectId":"1FgtD6WT8Y",
"ACL":{
"*":{
"read":true
},
"cryZoU5gXJ":{
"write":true,
"read":true
}
}
}
]
}
When I call the following line of code...
ds = Newtonsoft.Json.JsonConvert.DeserializeObject(Of DataSet)(strJSON)
I get an exception with the message stating "Specified argument was out of the range of valid values"
The JSON string is created with the following REST API call to Parse.com.
strJSON = http.QuickGetStr(strURL)
I am using this elsewhere with success albeit with simpler Parse classes but I have gone through this JSON string carefully and can't see anything wrong.
Any ideas on what might be causing this error?
In order for Json.Net to deserialize into a DataSet, the JSON must be in a specific format, as described in this answer. Your JSON is close, but the problem is the ACL object. The DataTableConverter that Json.Net 5.0 uses expects all of the columns in the table to be simple data types or it will throw an ArgumentOutOfRangeException (source). Json.Net 6.0 supports nested data tables and arrays in addition to simple types, but your ACL data still does not meet the required format that would allow it to be deserialized correctly to a DataSet. You have a few different options for dealing with this:
Change the JSON
If you control the format of the JSON (i.e. it is not from a third party) you can change it such that Json.Net 6.0 will be able to deserialize it to a DataSet. Here is what it would need to look like for that to work:
{
"results": [
{
"templateName": "HUD Section 8",
"userID": "2",
"mobileObjectId": "4582",
"source": "M",
"inspectionType": "A",
"notes": "Window in bedroom needs repair.",
"agencyID": "",
"requestDate": "2014-05-09 00:00:00",
"agencyName": "",
"inspectionTimeBegun": "2014-05-09 14:00:17",
"inspectionDate": "2014-05-09 14:30:00",
"inspectionID": 135,
"inspectionTimeComplete": "2014-05-09 14:29:25",
"summaryDecision": "F",
"createdAt": "2014-05-09T18:29:35.050Z",
"updatedAt": "2014-05-09T18:29:35.050Z",
"objectId": "1FgtD6WT8Y",
"ACL": [
{
"user": "*",
"read": true,
"write": false
},
{
"user": "cryZoU5gXJ",
"read": true,
"write": true
}
]
}
]
}
With this format, the ACL column of the results table will contain a nested DataTable with the individual ACL rows, each row having three columns, user, read and write.
Deserialize to strongly-typed classes
Instead of deserializing into a DataSet, you could deserialize into a set of strongly-typed classes. The advantage to this approach is that everything is in an easily usable form. The disadvantage is that you need to know what is in the JSON before you can create the classes.
You can use third-party tools like json2csharp.com to help generate the classes from a sample of the JSON, as was suggested in another answer (now deleted), but note that this is not foolproof (and it doesn't do VB). Sometimes you will need to intervene and edit the classes manually. For example, if generate classes from the JSON in your question, you'll notice that it creates a fixed class for each ACL instance. This will not work unless your set of ACLs always has exactly two items, one called Everyone and the other CryZoU5gXJ. I think it is much more likely that the set of ACLs will be variable, so it makes sense to use a Dictionary for these. Here are the classes I would propose:
Class RootObject
Public Property results As List(Of Result)
End Class
Class Result
Public Property templateName As String
Public Property userID As String
Public Property mobileObjectId As String
Public Property source As String
Public Property inspectionType As String
Public Property notes As String
Public Property agencyID As String
Public Property requestDate As String
Public Property agencyName As String
Public Property inspectionTimeBegun As String
Public Property inspectionDate As String
Public Property inspectionID As Integer
Public Property inspectionTimeComplete As String
Public Property summaryDecision As String
Public Property createdAt As String
Public Property updatedAt As String
Public Property objectId As String
Public Property ACL As Dictionary(Of String, ACL)
End Class
Class ACL
Public Property read As Boolean
Public Property write As Boolean
End Class
With this class structure in place, you can deserialize like this:
Dim root As RootObject = JsonConvert.DeserializeObject(Of RootObject)(strJSON)
For the ACLs, the key for each dictionary entry will be the user ID (or * as you have in your example). If you don't actually care about the ACLs, you can simply omit the ACL property from the Result class. By default Json.Net will skip properties that exist in the JSON but do not exist in the class.
Use the LINQ-to-JSON API to parse the JSON
With Json.Net there is always more than one way to skin the cat. Json.Net's LINQ-to-JSON API really shines when the JSON you are parsing is highly variable and/or you don't want to create classes for receiving the data. You can deserialize any valid JSON to a hierarchy of JToken objects and then pick them apart however you need. For example, if you just needed a few select pieces of information from each result, you could do this:
Dim token As JToken = JToken.Parse(json)
For Each result As JObject In token("results").Children(Of JObject)()
Console.WriteLine("userID: " + result("userID").ToString())
Console.WriteLine("templateName: " + result("templateName").ToString())
Console.WriteLine("inspectionID: " + result("inspectionID").ToString())
Console.WriteLine("inspectionType: " + result("inspectionType").ToString())
Console.WriteLine("inspectionDate: " + result("inspectionDate").ToString())
Console.WriteLine("summaryDecision: " + result("summaryDecision").ToString())
Console.WriteLine("notes: " + result("notes").ToString())
Next
You could use this same approach to manually build a DataSet from the JSON. Here is a generic function that will deserialize JSON into a DataSet but ignore any complex objects (e.g. the ACLs) instead of throwing an exception:
Function DeserializeToDataSet(json As String) As DataSet
Dim root As JObject = JObject.Parse(json)
Dim ds As DataSet = New DataSet()
For Each prop As JProperty In root.Properties
If prop.Value.Type = JTokenType.Array Then
Dim dt As DataTable = ds.Tables.Add(prop.Name)
For Each row As JObject In prop.Value.Children(Of JObject)()
Dim dr As DataRow = dt.NewRow
For Each col As JProperty In row.Properties
Dim colType As Type = GetColumnType(col.Value.Type)
If Not colType Is Nothing Then
Dim dc As DataColumn = dt.Columns(col.Name)
If dc Is Nothing Then
dc = dt.Columns.Add(col.Name, colType)
End If
dr(col.Name) = col.Value.ToObject(colType)
End If
Next
dt.Rows.Add(dr)
Next
End If
Next
Return ds
End Function
Function GetColumnType(tokenType As JTokenType) As Type
If tokenType = JTokenType.String Then Return GetType(String)
If tokenType = JTokenType.Integer Then Return GetType(Integer)
If tokenType = JTokenType.Date Then Return GetType(DateTime)
If tokenType = JTokenType.Boolean Then Return GetType(Boolean)
If tokenType = JTokenType.Float Then Return GetType(Double)
Return Nothing
End Function
Of course if you need the ACLs, you'll need to customize this method to get that data into a form that is consumable by your code. I'll leave that part to you.
Json.Net will only parse directly into a DataSet if it conforms to a certain standard. See this answer for the layout it needs.
However, you could deserialize to an XML document and use the DataSet object's ReadXml method load it for you. See this question for details on how to do this.
(HT to Brian Rogers for dataset structure details)