Access deserialize JSON and avoid var declaration - vb.net

I'm completely new to vb (started about 2 hours ago) and I'm trying to convert a php application to vb to circumnavigate some unavoidable problems.
However I'm trying to get a response from a server that returns a JSON string as page source(everythng is fine untill here), my problem is that I don't understand exactly how to access the deserialized object.
This is the response:
{
"response":{
"a":"boolean",
"b":"string",
"c":"string",
"d":"string",
"e":"string",
"f":"string",
"profile":{
"h":"decimal",
"i":"string",
"l":"string",
"m":"string",
"n":"string",
"o":"string",
"p":"string",
"q":"string"
}
}
}
Current vb code:
Public Class Form1
...
Dim jsonResponse As String = New System.Net.WebClient().DownloadString(url)
Dim r As LoginReturn = JsonConvert.DeserializeObject(Of LoginReturn)(jsonResponse)
...
End Sub
Public Class LoginItem
Public a As Boolean
Public apikey As String
Public c As String
Public d As String
Public e As String
Public f As String
Public Property profile As List(Of LoginProfile)
End Class
Public Class LoginProfile
Public h As Decimal
Public i As String
Public l As String
Public m As String
Public n As String
Public o As String
Public p As String
Public q As String
End Class
Public Class LoginResponse
Public Property response As List(Of LoginItem)
End Class
Public Class LoginReturn
Public Property value As List(Of LoginResponse)
End Class
From all those infomation I only need apikey so I tried to access it with these
r.value.response.apikey
r.value(0).response.apikey
Both returns this error:
'apikey' is not a memeber of 'System.Collections.Generic.List(Of WindowsApplication1.LoginItem)'.
Previously with php I used this:
$r = json_decode(file_get_contents($url));
$_SESSION['a']=$r->response->apikey
So my questions are:
How do I access that information?
Do I need to declare all those variables even if I don't need them?
EDIT SOLUTION
'Get Json
Dim jsonResponse As String = New System.Net.WebClient().DownloadString(url)
'Parse Json
Dim r As JObject = JObject.Parse(jsonResponse)
'Access Json
GlobalVar.api = r("response")("apikey")

This will show you how to parse the raw JSON so you can pluck out the part you are interested in. Since you only want one thing, you can use JObject.Parse and forego the classes to get what you want.
' you would get it from the webclient
Dim jstr As String = File.ReadAllText("C:\Temp\logresp.json")
Dim api = obj("response")("b")
To read something else, just change the key to whatever item you want. For instance, to read "q":
Dim QItem = obj("response")("profile")("q")
Since q is a property of profile, which itself is a child of response you have to dig deeper.
If you do end up needing many things, your classes are not quite right for parsing into a class (DeserializeObject). Look at the JSON and you'll see there is no property named "APIKey". Going by position, it is called "b". This might be an artifact of changing things around for posting here, but I can only go by what is posted.

Related

How to deal with complex json data in vb.net webservice

I am (trying) to create a RestApi in VB.net and have hit a dead end when it comes to posting data to my endpoint. If i use a simple json file post it via postman and bind it to my data class it is fine but once the Data class becomes more complex it no longer works. By that i mean a class like below works
<First>
<Last>
<AddressLine1>
<City>
<State>
but
<UserInfo>
<Name>
<First>
<Last>
</Name>
<Address>
<AddressLine1>
<City>
<State>
</Address>
</UserInfo>
does not. So here are my 2 question
A) Is there a way to have the build in Parser handle more complex data classes like my above since this doesnt work
Public Function PostValue(<FromBody()> ByVal value As User_Info) As String
If ModelState.IsValid Then
Console.Write(value.address.CITY)
Return "OK"
Else
Return "Error"
End If
End Function
b) what is the alternate way to access the json posted in body so i can manually
assign the values to my complex data class ?
Your sample is not JSON. It's very easy if you just use the built-in functionality.
Here is what your class would look like
<DataContract()>
Class UserInfo
<DataMember>
Public Name As New Name
<DataMember>
Public Address As New Address
End Class
<DataContract()>
Class Name
<DataMember>
Public First As String
<DataMember>
Public Last As String
End Class
<DataContract()>
Class Address
<DataMember>
Public AddressLine1 As String
<DataMember>
Public City As String
<DataMember>
Public State As String
End Class
Here's an example that save to JSON and loads it back.
Imports System.IO
Imports System.Runtime.Serialization.Json
Imports System.Runtime.Serialization
Dim ui As New UserInfo
' Initialize
ui.Name.First = "f"
ui.Name.Last = "l"
ui.Address.AddressLine1 = "a"
ui.Address.City = "c"
ui.Address.State = "s"
' Write to stream
Dim stream As New MemoryStream
Dim ser As New DataContractJsonSerializer(GetType(UserInfo))
ser.WriteObject(stream, ui)
' Show data
stream.Position = 0
Dim sr = New StreamReader(stream)
Dim jsonData As String = sr.ReadToEnd()
Console.WriteLine(jsonData)
' Bring it back
Dim ui2 As UserInfo
stream.Position = 0
ui2 = ser.ReadObject(stream)
Console.WriteLine(ui.Name.First)
Console.WriteLine(ui.Name.Last)
Console.WriteLine(ui.Address.AddressLine1)
Console.WriteLine(ui.Address.City)
Console.WriteLine(ui.Address.State)
This would be the JSON generated
{
"Address":
{
"AddressLine1":"a",
"City":"c",
"State":"s"
},
"Name":
{
"First":"f",
"Last":"l"
}
}

Using classes for JSON serialization

All,
I need some help with understanding how classes can work with vb.NET and JSON.NET. I'm completely new to this. I've tried searching for answers, but I'm probably not asking the right questions. Here's my dilemma:
I have a JSON that I need to send to a REST API.
{
"paInfo":[
{
"providerAccountName":"someClient",
"providerAccountDescription":"A fine client.",
"providerName":"provider",
"externalProviderIdentifier":"BU4377890111"
},
{
"providerAccountName":"someClient1",
"providerAccountDescription":"A fine client.",
"providerName":"provider",
"externalProviderIdentifier":"BU4377890111"
}
],
"hubAccountName":"test"
}
I ran this through https://jsonutils.com/ to build my class as:
Public Class PaInfo
Public Property providerAccountName As String
Public Property providerAccountDescription As String
Public Property providerName As String
Public Property externalProviderIdentifier As String
End Class
Public Class addHubAcct
Public Property paInfo As PaInfo()
Public Property hubAccountName As String
End Class
From there, I'm trying to assign values to the class properties, but I don't quite understand how to pass the values for PaInfo to the property. Below is a snippet of code I'm using to assign values. If I try to assign a.paInfo = p, it errors:
error BC30311: Value of type 'PaInfo' cannot be converted to
'PaInfo()'
If I don't pass anything through to a.paInfo, I get a zero-length string in the JSON serialization.
Private Sub serializeAcct()
Dim p As New PaInfo
Dim a As New addHubAcct
p.providerAccountName = "Test\name'This ""that and the other'"
p.providerAccountDescription = "acct desc"
p.providerName = "tester"
p.externalProviderIdentifier = "123456"
a.hubAccountName = "Tester"
a.paInfo = p 'Here's my hangup
Dim o As String = JsonConvert.SerializeObject(a)
Dim deserializedProduct As addHubAcct = JsonConvert.DeserializeObject(Of addHubAcct)(o)
Stop
End Sub
?o.tostring,nq
{"paInfo":null,"hubAccountName":"Tester"}
Change the addHubAcct class like this:
Public Class addHubAcct
Public Property paInfo As New List(Of PaInfo)()
Public Property hubAccountName As String
End Class
And then change the bad line in serializeAcct() like this:
a.paInfo.Add(p)
You likely have other problems as well, but that should get you past the current obstacle.
Using List and .ToArray is what I was missing with my original code.
Private Sub serializeAcct()
Dim p1 As New PaInfo
Dim ps As New List(Of PaInfo)
Dim a As New addHubAcct
p1.providerAccountName = "Test\name'This ""that and the other'"
p1.providerAccountDescription = "acct desc"
p1.providerName = "tester"
p1.externalProviderIdentifier = "123456"
ps.Add(p1)
a.hubAccountName = "Tester"
a.paInfo = ps.ToArray
Dim o As String = JsonConvert.SerializeObject(a)
End Sub

Download Files From Web Server

I have the following response from a URL. How do I code to download the two files to my hard drive with the name = id.
HTTP/1.1 200 OK
Content-Type: application/json
{
"files": [
{
"format": "fillz-order-tab",
"checksum": "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
"acknowledged": false,
"uri": "https://file-api.fillz.com/v1/orders/created/20140611T003336Z-8b975127",
"date_created": "20140611T003336Z",
"id": "20140611T003336Z-8b975127"
},
{
"format": "fillz-order-tab",
"checksum": "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35",
"acknowledged": false,
"uri": "https://file-api.fillz.com/v1/orders/created/20140611T013545Z-3e2f2083",
"date_created": "20140611T013545Z",
"id": "20140611T013545Z-3e2f2083"
}
]
}
My code that calls the URL is the following:
Using response As HttpWebResponse = TryCast(request.GetResponse(), HttpWebResponse)
Dim reader As New StreamReader(response.GetResponseStream())
result = reader.ReadToEnd()
I am using json.net with visual basic 2008.
These are my classes
Public Class file
Public format As String
Public checksum As String
Public acknowledged As String
Public uri As String
Public date_created As String
Public id As String
End Class
Public Class RootObject
Public Property files() As List(Of file)
Get
End Get
Set(ByVal value As List(Of file))
End Set
End Property
End Class
This is my code to deserializare json results
Dim res As RootObject = JsonConvert.DeserializeObject(Of FillzAPI.FileAPI.RootObject)(result)
I want to read each id from the url response
For Each Data As FileAPI.RootObject In res
Next
I have the next error:
Expression is of type 'FillzAPI.FileAPI.RootObject', which is not a collection type.
How do I fix this error?
A few points which will give you working code:
There is an easier way to download the JSON data.
You've accidentally
declared RootObject.files as an array of lists, and its Get and
Set methods are empty.
File is an unfortunate name for a class as
it conflicts with System.IO.File.
It would be better to have the
things in (what I named) FileData as properties. You can take
advantage of auto-declared properties and not have to type the
Get/Set methods.
Putting all those together, I arrived at
Option Infer On
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json
Module Module1
Public Class FileData
Public Property format As String
Public Property checksum As String
Public Property acknowledged As String
Public Property uri As String
Public Property date_created As String
Public Property id As String
End Class
Public Class RootObject
Public Property Files As List(Of FileData)
End Class
Sub Main()
' I set this up on a local web server. Adjust as required.
Dim src = "http://127.0.0.1/JsonSample.txt"
' An easy way to get a string from a web server...
Dim wc As New WebClient
'TODO: Try..Catch any error that wc.DownloadString throws.
Dim jsonData = wc.DownloadString(src)
'TODO: Try..Catch any error that JsonConvert.DeserializeObject throws.
Dim y = JsonConvert.DeserializeObject(Of RootObject)(jsonData)
' Somewhere to save the downloaded files...
Dim dest = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "My JSON test")
If Not Directory.Exists(dest) Then
Directory.CreateDirectory(dest)
End If
For Each x In y.Files
Console.WriteLine(x.uri)
'TODO: Try..Catch any error that wc.DownloadFile throws. Also perhaps use async methods.
wc.DownloadFile(x.uri, Path.Combine(dest, x.id))
Next
Console.ReadLine()
End Sub
End Module
Which outputs
https://file-api.fillz.com/v1/orders/created/20140611T003336Z-8b975127
https://file-api.fillz.com/v1/orders/created/20140611T013545Z-3e2f2083
and saves the files.

.net - Using Class as one parameter

I have a class with several properties.
Public Class test
Public Property a As String
Public Property b As String
Public Property c As String
Public Property d As String
Public Property e As String
Public Property f As String
Public Property g As String
End Class
In my VB.net code, I am assigning a value to each property.
I want to send the whole test class as one parameter, and use all the values inside it.
So that if I add extra parameters later on, I want them to be used dynamically, instead of writing this everytime:
Textbox1.text= test.a & test.b & test.c .......
Any way to do this?
Im not really writing the values in a textbox, but this is just an simplified example.
I think what you want is a property. You'll need to add a property to your class like:
Public Property Combination() As String
Get
Return a & b & c & d & e ...
End Get
End Property
Then to get the value you'd use
Textbox1.text = test.combination
(for more details you can see http://www.dotnetperls.com/property-vbnet)
I recommend you override the built-in ToString function. Also, to further simplify this, add a CType operator.
Public Class test
Public Property a As String
Public Property b As String
Public Property c As String
Public Property d As String
Public Property e As String
Public Property f As String
Public Property g As String
Public Shared Widening Operator CType(obj As test) As String
Return If((obj Is Nothing), Nothing, obj.ToString())
End Operator
Public Overrides Function ToString() As String
Return String.Concat(Me.a, Me.b, Me.c, Me.d, Me.e, Me.f, Me.g)
End Function
End Class
The you could just do:
Textbox1.text = test
There is a way to dynamically get and set the value of properties on any object. Such functionality in .NET is collectively referred to as Reflection. For instance, to loop through all of the properties in an object, you could do something like this:
Public Function GetPropertyValues(o As Object) As String
Dim builder As New StringBuilder()
For Each i As PropertyInfo In o.GetType().GetProperties
Dim value As Object = Nothing
If i.CanRead Then
value = i.GetValue(o)
End If
If value IsNot Nothing Then
builder.Append(value.ToString())
End If
Next
Return builder.ToString()
End Function
In the above example, it calls i.GetValue to get the value of the property, but you can also call i.SetValue to set the value of the property. However, reflection is inefficient and, if used inappropriately, it can lead to brittle code. As such, as a general rule, you should avoid using reflection as long as there is any other better way to do the same thing. In other words, you should typically save reflection as a last resort.
Without more details, it's difficult to say for sure what other options would work well in your particular situation, but I strongly suspect that a better solution would be to use a List or Dictionary, for instance:
Dim myList As New List(Of String)()
myList.Add("first")
myList.Add("second")
myList.Add("third")
' ...
For Each i As String In myList
Textbox1.Text &= i
Next
Or:
Dim myDictionary As New Dictionary(Of String, String)()
myDictionary("a") = "first"
myDictionary("b") = "first"
myDictionary("c") = "first"
' ...
For Each i As KeyValuePair(Of String, String) In myDictionary
Textbox1.Text &= i.Value
Next

Unable to cast custom listviewitem class in vb.net

This is a followup question to another question I asked earlier. I thought I had everything I needed, but I'm running into another issue. I'm trying to use a custom listviewitem class that attaches additional information to a lisview item. Here is the class:
Public Class albumListViewItem
Inherits ListViewItem
Public hash As String
Public id As Integer
Public provider As String
Public providerID As String
Public providerURL As String
Public providerArtistID As String
Public albumName As String
Public albumType As String
Public numTracks As Integer
Public imageURLs() As String
Public genres() As String
Public styles() As String
Public label As String
Public year As String
Public country As String
Public rating As String
Public editorsPick As Boolean
Public sampleStreamURL As String
Public providerReview As String
End Class
When I try to cast a listviewitem to my custom class like this:
Dim albumItem As albumListViewItem = CType(lsvHidden.items.item(0), albumListViewItem)
I get the following error, "Unable to cast object of type 'System.Windows.Forms.ListViewItem' to type 'AudioMatic.albumListViewItem'."
What am I missing here?
From your previous question and this one, I think a better fit for your problem would be to use a regular ListViewItem and store the accessory information in ListViewItem.Tag
You can do
Dim listViewItem As New ListViewItem("SomeText")
Dim albumInfo As New albumListViewItem()
albumInfo.albumName = "SomeAlbum"
...
listViewItem.Tag = albumInfo
listView1.Items.Add(listViewItem)
and then retrieve it like this
Dim selectedItem As ListViewItem = listView1.SelectedItems(0).Item
Dim alubmInfo As albumListViewItem = TryCast(selectedItem.Tag, alubmListViewItem)
Dim albumName as String = albumInfo.albumName
see if this solution will work for you.
If you step through the code and watch the variable "lsvHidden.items.item(0)" you should be able to first tell if it is in fact of type "albumListViewItem" or something else. Are you sure it was albumListViewItem that was added to the list in the first place?
Some alternatives to what you are doing;
1. You can implement an object and store it in the tag of the ListViewItem.
2. The following article seems to describe another approach of adding Columns to the listview to allow storing extra information on the listview itself; http://www.codeproject.com/KB/list/ListViewExtendedItem.aspx
I can appreciate your situation as I would have expected that to work. And I can see advantages and reasons for doing it that way as well. Not sure if the code project sample is adaptable to what you need, so you'll need to review the concept.
The working code:
Public Class albumListViewItem
Inherits ListViewItem
Public hash As String
Public id As Integer
Public provider As String
Public providerID As String
Public providerURL As String
Public providerArtistID As String
Public albumName As String
Public albumType As String
Public numTracks As Integer
Public imageURLs() As String
Public genres() As String
Public styles() As String
Public label As String
Public year As String
Public country As String
Public rating As String
Public editorsPick As Boolean
Public sampleStreamURL As String
Public providerReview As String
End Class
Storing information using listviewitem.tag:
Dim listViewItem As New ListViewItem("SomeText")
Dim albumItem As New albumListViewItem
albumItem.albumName = "Test Album"
albumItem.id = "testid"
albumItem.Text = albumItem.albumName
albumItem.year = "2011"
albumItem.numTracks = 10
'....
listViewItem.Tag = albumItem
'add viewable items to listview
albumItem.SubItems.Add(albumItem.year)
albumItem.SubItems.Add(albumItem.numTracks)
'....
ListView1.Items.Add(albumItem)
Reading the information that was previously stored:
Dim albumInfo As albumListViewItem = CType(ListView1.SelectedItems(0), albumListViewItem)
Dim id as string = alumInfo.id