Deserializing Json VB.NET - json

I am trying to deserialize data from a webserver for a game launcher.
The API docs provide me the data in json format, but I have been struggling to be able to read that data I am given and store it as a variable to use to log in a player. I have also tried a few other things but I am just stuck now. Normally a response from the server would like like this:
{"success":"true","gameserver":"gameserver-alpha.toontownrewritten.com","cookie":"deadbeefdeafbeef0x123"}
The code to deserialize the data:
Dim result() As TTRServerResponse = JsonConvert.DeserializeObject(Of TTRServerResponse())(responseFromServer)
Class with the variables I want to store
Public Class TTRServerResponse
Public Property success As String
Public Property eta As String
Public Property position As String
Public Property queueToken As String
Public Property cookie As String
End Class
Any ideas where I messed up or what I should do? Thanks, Ben.
EDIT: Finally figured it out, I needed to change my result to: Dim result As TTRServerResponse = JsonConvert.DeserializeObject(Of TTRServerResponse) (responseFromServer) I also was returning the data incorrectly to my other sub by returning just result. What I needed to do was return result.success. I now have a better understanding.

You are trying to deserialize the JSON into an array but in your example it is a single object.
So assuming
Dim responseFromServer As String
is equal to
{"success":"true","gameserver":"gameserver-alpha.toontownrewritten.com","cookie":"deadbeefdeafbeef0x123"}
Which, according to the documentation you linked to, is suppose to be a single object,
then you need to update your code to deserialize a single object as appose to an array
Dim result As TTRServerResponse = JsonConvert.DeserializeObject(Of TTRServerResponse)(responseFromServer)

Related

How to deserialize Json with fix named elements dynamically in vb.net?

I’m on the way to implement an interface to the Ameritrade Rest API in a vb.net application (with httpclient).
Amongst other things, I have to query quotes from a ticker list (e.g. AMD,MSFT,AMZN, ....).
The call of the API works without problems, I get a valid Json back, but the Json is not given back in a way, I would expect.
I now search the best way to handle that problem...
This is not the first interface to a Rest API, I have implemented.
Normally, I implement a corresponding data class in vb.net and then use JsonConvert (from Newtonsoft) to deserialize the Json string into my data class.
Example:
Dim oObject As New DataClass
oObject = JsonConvert.DeserializeObject(Of DataClass)(JsonString)
whereby DataClass is the vb.net class that is defined according to the data in the Json string.
Problem:
The ticker symbol-list to query is dynamic and can change from api call to api call.
If I - e.g. - query AMD and MSFT in a call, I get back (cut to only a few fields) the following Json:
{
"AMD": {
"assetType": "EQUITY",
"symbol": "AMD",
"description": "Advanced Micro Devices, Inc. - Common Stock",
"bidPrice": 92.11
},
"MSFT": {
"assetType": "EQUITY",
"symbol": "MSFT",
"description": "Microsoft Corporation - Common Stock",
"bidPrice": 243.1
}
}
To be able to deserialize the Json, I would have to implement the following DataClass:
Public Class DataClass
Public Property AMD As AMD
Public Property MSFT As MSFT
End Class
Public Class AMD
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
Public Class MSFT
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
This would work but is absolutely static and does not make any sense, as I would have to implement a (identical) class for any ticker, I maybe want to query in the feature.
I would expect to get back a dynamic list so that I could implement the class as following:
Public Class DataClass
Public Property TickerDetails As List(Of TickerDetail)
End Class
Public Class TickerDetail
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
This way, I would be able to deserialize in a List of TickerDetails and the go thru the list (no matter, which symbols I queried).
But, I can’t change, what I get back over the API...
Question:
What is the best way to handle this problem?
You should create a class to represent the a generic stock and then use DeserializeObject to deserialize it into a Dictionary(Of String, [classname]) where the Key represents the stock symbol and the value represents the class.
Take a look at this example:
Public Class Stock
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
'...
Dim stocks = JsonConvert.DeserializeObject(Of Dictionary(Of String, Stock))(response)
Example: Live Demo
First thanks for the comments.
I ended up to do it completely different now...
I had further problems with the Ameritrade API:
Some fields are named with leading numbers (52WkHigh and 52WkLow) and
vb.net dev's know, that VB.net don't like properties in classes that
are named with a leading number
So I had to "patch" the received Json data and change the names on the fly to other names ("52WkHigh" to "dble52WkHigh" and "52WkLow" to "dble52WkLow") to be able to deserialize
over the data class, what is not nice
Further, I finally need the data (as fast as possible) in a data table and had "a long way to go":
get data -> deserialize to the data class -> walk thru the data class and overtake the data in the data table.
So.. my new solution (with JObject):
Note: needs:
Imports Newtonsoft.Json.Linq
Code snippets:
Create data table in memory:
Dim dtErgebnis As New DataTable
Dim drTemp As DataRow
With dtErgebnis.Columns
.Add("symbol", System.Type.GetType("System.String"))
.Add("lastPrice", System.Type.GetType("System.Double"))
.Add("lastSize", System.Type.GetType("System.Int32"))
.Add("quoteTime", System.Type.GetType("System.DateTime")) ' Note: is a Long in Json
...
End With
Parse the Json-String and fill the datatable:
get the data over httpclient (in JsonString)...
Dim oJson As JObject = JObject.Parse(JsonString) ' creates children tokens
Dim results As List(Of JToken) = oJson.Children().ToList
For Each item As JProperty In results
item.CreateReader()
drTemp = dtErgebnis.NewRow() ' create a new row to data table in memory
' Fill the fields
drTemp("symbol") = item.Value("symbol")
drTemp("lastPrice") = item.Value("lastPrice")
drTemp("lastSize") = item.Value("lastSize")
drTemp("quoteTime") = GetUTCDateFromTimeStamp(item.Value("quoteTimeInLong")).AddHours(1) ' original Long
...
' Add the new row to the data table
dtErgebnis.Rows.Add(drTemp)
' Save the changes
dtErgebnis.AcceptChanges()
Next
Additional note: The Ameritrade API gives back the time stamps as long (additional hurdle), but I (and I think also you;-) want it as datetime.
Therefore the Long (I think this data type comes from Java/Unix) has to be "translated" to datetime = vb.net function GetUTCDateFromTimeStamp below:
Public Function GetUTCDateFromTimeStamp(TimeStamp As Long) As DateTime
Static startTime As New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
Return startTime.AddMilliseconds(TimeStamp)
End Function
Additional note:
As I want to have the Swiss time, I add one hour to the UTC time.
So.. this a real good solution for me (exactly for the Ameritrade API).
And.. it's blazing fast... (I get 19 tickers with all fields and show the result (data table) in a data grid).
All together took < 1 Second ("felt" about 500 ms)
Hope this helps somebody...

Handling Json Array in VB.net

I am getting data from some monitoring equipment which is returning what I believe is a Json (nested) array but the syntax is not quite right.
Below is a sample of what is being retuned. Its an array of UnixTimeMilliseconds and a value.
[[1579617389000,132],[1579617399000,136],[1579617409000,139],[1579617419000,137]]
It could be up to 3000 sets of these numbers.
What I would like to do is dump them into an object of some sort an array so I am able to work with it e.g. translate the UnixTimeMilliseconds into datetime and do calculations.
Last thing I tried was:
Dim ListResultsArray As Directory()
ListResultsArray = JsonConvert.DeserializeObject(Of Directory())(emoncmsResponse)
and the error returned was:
Could not create an instance of type System.IO.Directory. Type is an interface or abstract class and cannot be instantiated. Path '[0].id', line 1, position 7.
A simple method to convert the Arrays is to parse the JSON with JArray, iterate the inner arrays and produce a new class object, converting the array values to the desired type.
Create an object that can represent the array values:
<JsonArray>
Public Class NumbersItem
Public Sub New(objTime As DateTimeOffset, objValue As Long)
DetectionTime = objTime
Value = objValue
End Sub
Public ReadOnly Property DetectionTime As DateTimeOffset
Public ReadOnly Property Value As Long
End Class
Then parse the JSON and generate new class objects from each inner JArray object:
Since the first value in the Array represent an Unix DateTime expressed in milliseconds, we can use the DateTimeOffset.FromUnixTimeMilliseconds() method to convert it.
Dim numbers As New List(Of NumbersItem)()
Dim jsonArrays = JArray.Parse(json)
For Each array As JArray In jsonArrays.Children()
numbers.Add(New NumbersItem(
DateTimeOffset.FromUnixTimeMilliseconds(CType(array.First, Long)),
CType(array.Last, Long)))
Next
Now, you can inspect each NumbersItem class object in the list as usual.
The DetectionTime property is defined as a DateTimeOffset because the Unix time is expressed in UTC coordinates. You can use the DateTimeOffset methods to determine the local DateTime (DateTimeOffset.ToLocalTime) or perform other conversions.
Have you tried JObject using NewtonSoft.json.Linq
Just create a JObject Array and parse Json
Dim object As JObject()
JObject.parse(object(index))

JSON deserialization error with Azure translation services

I am building a program in Visual Studio 2017 in Windows Forms - sorry but that's the only thing I know how to use - anyway, most everything for this is C#, so I've been having trouble getting help.
I have translated the Microsoft provided example for a C# program to connect to Azure Cognitive Translation services, signed up, got all my keys, etc.
When I run the code, I get the following error:
Newtonsoft.Json.JsonSerializationException:
'Cannot deserialize the
current JSON object (e.g. {"name":"value"}) into type
System.Collections.Generic.List1[System.Collections.Generic.Dictionary2[System.String,System.Collections.Generic.List1[System.Collections.Generic.Dictionary2[System.String,System.String]]]]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly.
To fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize from a JSON object. Path 'error', line 1, position 9.'
I have tried too many things to list from many different sources. I do not know a whole lot about JSON and am asking for help with the code to solve the above issue.
Public Class DetectedLanguage
Public Property language As String
Public Property score As Double
End Class
Public Class Translation
Public Property text As String
Public Property two As String
End Class
Public Class Example
Public Property detectedLanguage As DetectedLanguage
Public Property translations As Translation()
End Class
Dim textToTranslate As String = root
Dim fromLanguage As String
Dim fromLanguageCode As String = cabbr
Dim toLanguageCode As String = "en"
Dim endpoint As String = String.Format(TEXT_TRANSLATION_API_ENDPOINT, "translate")
Dim uri As String = String.Format(endpoint & "&from={0}&to={1}", fromLanguageCode, toLanguageCode)
Dim body As System.Object() = New System.Object() {New With {Key .Text = textToTranslate}}
Dim requestBody = JsonConvert.SerializeObject(body)
Using client = New HttpClient()
Using request = New HttpRequestMessage()
request.Method = HttpMethod.Post
request.RequestUri = New Uri(uri)
request.Content = New StringContent(requestBody, Encoding.UTF8, "application/json")
request.Headers.Add("Ocp-Apim-Subscription-Key", COGNITIVE_SERVICES_KEY)
request.Headers.Add("Ocp-Apim-Subscription-Region", "westus")
request.Headers.Add("X-ClientTraceId", Guid.NewGuid().ToString())
Dim response = client.SendAsync(request).Result
Dim responseBody = response.Content.ReadAsStringAsync().Result
Dim result = JsonConvert.DeserializeObject(Of List(Of Dictionary(Of String, List(Of Dictionary(Of String, String)))))(responseBody)
Dim translation = result(0)("translations")(0)("text")
rtRoot.Text = translation
End Using
End Using
I have already used the jsonutil site to paste my JSON code in and get the classes.
Here is my JSON content:
[
{
"detectedLanguage":{
"language":"nl",
"score":1.0
},
"translations":[
{
"text":"bord vervangen en uitvoerig getest",
"to":"nl"
},
{
"text":"Board replaced and tested extensively",
"to":"en"
}
]
}
]
OK!!! after playing around with this - Jimi - your solution worked!!! thank you SO much! i had to remove the following to lines: request.Headers.Add("Ocp-Apim-Subscription-Region", "westus") request.Headers.Add("X-ClientTraceId", Guid.NewGuid().ToString())

Attach JSON data to be used by view in MVC application

I have a very simple application that I am using to learn move about MVC. In that application I search some data and return a JSON string that I want to use as the model for the view. I just can't seem to figure out how to get the view to consume the JSON as a set of data for it to show on screen.
The code I have so far:
Function Find(term As String) As ActionResult
Dim model As String = SearchData(term)
Return View(model)
End Function
SearchData returns a JSON string that can have one or many objects in it.
How do I now take the JSON returned from SearchData and use it in a view? In fact, when I attempt to add a view it wants to know what model to use. How do i also specific that?
Create a strongly typed object to store you data when parsed
Public Class Data
Public Property ID As Integer
Public Property Term As String
Public Property SomeProperty As String
Public Property SomeOtherProperty As String
End Class
Using a library like JSON.Net, parse the JSON returned from the search.
This assumes a collection of Data is returned from the search.
Imports Newtonsoft.Json;
Function Find(term As String) As ActionResult
Dim json As String = SearchData(term)
Dim model As List(Of Data) = JsonConvert.DeserializeObject(Of List(Of Data))(json)
Return View(model)
End Function
Let the view know to expect the strongly typed model.
#ModelType List(Of Data)
#Code
ViewData("Title") = "Find"
End Code
<h2>MyView</h2>
<!-- rest of view where model can be accessed -->

Parsing JSON Objects within JSON arrays within JSON Objects in VB.Net

Hello Everyone and thanks for looking at this. I'm relatively new to vb.net and extremely new to parsing json in vb. I am using JSON.Net and I'm looking to gather data from the following JSON.
http://hastebin.com/bagiyetece.apache
I have classes created for each of the "sections". I am unsure of the correct terminology.
Class One:
Public Class StatsWrapper
Public SummonerID as Long
Public PlayerStatSummaries as playerStatSummaryTypeWrapper
End Class
Class Two:
Public Class playerStatSummaryTypeWrapper
Public playerStatSummaryType As String
Public wins As Long
Public modifyDate As Long
Public aggregatedStats As aggregatedStatsWrapper
End Class
Class Three:
http://hastebin.com/qopanafabe.php
My end goal is to be able to get elements like "totalChampionKills" for the playerStatSummaryType of "Cap5x5" and insert them into a datagridview.
I've been able to correctly parse the following JSON simply by using JObject.Parse.
{"UserName":{"id":84737282,"name":"UserName","profileIconId":660,"summonerLevel":30,"revisionDate":1455686689000}}
To get the id object, I would use:
Dim JSONDerulo = JObject.Parse(JSONResponse)
Dim SummonerID = JSONDerulo(LCase(ToolStripTextBox1.Text))("id")
Where the ToolStripTextBox1.Text is the UserName.
When I try to apply the same logic as above to the larger JSON file in a different sub:
Dim JSONDerulo = JObject.Parse(JSONResponse)
Dim PlayerStatSummaries = JSONDerulo("playerStatSummaries")
Dim Jarray As JArray = PlayerStatSummaries
I can do something like:
For Each obj As JObject In Jarray
TextBox2.Text = TextBox2.Text + Environment.NewLine + obj.ToString + Environment.NewLine
Next
Or call Jarray(1) and parse that again but the list of game types is going to be different for each UserName. I do have a master list for every game type though:
AramUnranked5x5, Ascension, Bilgewater, CAP5x5, CoopVsAI, CoopVsAI3x3, CounterPick, FirstBlood1x1, FirstBlood2x2, Hexakill, KingPoro, NightmareBot, OdinUnranked, OneForAll5x5, RankedPremade3x3, RankedPremade5x5, RankedSolo5x5, RankedTeam3x3, RankedTeam5x5, SummonersRift6x6, Unranked, Unranked3x3, URF, URFBots
If I want to (for example) call AramUnranked5x5.TotalChampionKills or something similar would I have to create a class for each type?
Any suggestions or ideas?
Thanks Again
Note: the json posted is considerably shorter and simpler than the gruesome aggregatedStatsWrapper class linked to depicts! Without data, I cant say whether it is right or not.
Since the aggregatedStats is its own type, nothing very interesting will show for it in a DataGridView, just the type name. There are several ways to handle this. One is to hide the property from the DGV, then when they change selected/current rows, find the new one in the list and set player.aggregatedStats as the selected object in a property grid for a master-detail type view. Playerstatsummary:
Public Class Playerstatsummary
Public Property playerStatSummaryType As String
Public Property wins As Integer
Public Property modifyDate As Long
<Browsable(False)>
Public Property aggregatedStats As Aggregatedstats
Public Property losses As Integer
End Class
<Browsable(False)> will result in the TypeName not being shown in a DGV:
Dim jstr = File.ReadAllText("C:\Temp\myjsonfilename")
Dim jobj = JObject.Parse(jstr)
Dim players = JsonConvert.DeserializeObject(Of List(Of Playerstatsummary))(jobj("playerStatSummaries").ToString)
By Parsing it first, you can skip that outer container. jobj("playerStatSummaries").ToString passes the property data to be deserialized into a List.
You can display what you have very easily without having to loop at all:
dgv1.DataSource = players
It wont yet know about Aggregatedstats unless and until you work out that class exactly. Until then, the type name will display. The post mentions being interested in Cap5x5 only. In that case, a PropertyGrid might be a better UI mechanism (after you find that guy in the list). Result:
(This is from before I added <Browsable(False)> to the class). You could show aggregatedStats as detail like this:
Private Sub DataGridView1_SelectionChanged(...etc
If DataGridView1.SelectedRows.Count = 0 Then Return
Dim thisOne = DataGridView1.SelectedRows(0).Cells(0).Value.ToString
Dim player = players.FirstOrDefault(Function(f) f.playerStatSummaryType = thisOne)
If player IsNot Nothing Then
PropertyGrid1.SelectedObject = player.aggregatedStats
End If
End Sub
In case you are wondering, the date is almost certainly a Unix Epoch date, easily converted to .NET.