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

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"
}
}

Related

Refer to a Class from within a With VB.net

I have called clientdetails which I wish to return as a whole to the JSONConvert Method to Serialize for JSON.
I have created a Class that has the Property Types I require (TextA,TextB) etc.
I can refer to both TransactionCount and TransactionType because they are part of ClientDetails, however when I try to refer to TextA of Transactions it states that TextA is not a member of ClientDetails - this I know which is why I explicitly state .Transactions.TextA.
If I declare Transactions separate under a new variable then I am able to refer to them however I need them to all be declared under ClientDetails to pass to the Serializer.
Can anyone point me out what I'm doing wrong here? Still learning.
Public Class JSON
Public Shared Function SerializeObject()
Dim clientdetails As New ClientDetails() With {.TransactionCount = "1", .TransactionType = "Q", .Transactions.TextA} 'Unable to Refer to any Property of Transactions.
'Dim Trans As New Transactions()
'Trans.TextA= "Test"
Dim output As String = JsonConvert.SerializeObject(clientdetails, Newtonsoft.Json.Formatting.Indented)
Return output
End Function
End Class
Public Class ClientDetails
Public Property Transactions As New Transactions()
Public Property [TransactionType] As String
Public Property [TransactionCount] As Integer
End Class
Public Class Transactions
Public Property [RecordID] As String
Public Property No As String
Public Property TextA As String
Public Property TextB As String
Public Property Initial As String
Public Property Flag As String
Public Property Sex As String
Public Property Area As String
Public Property Type As String
Public Property IDNO As String
End Class
You can use this syntax:
Dim clientdetails As New ClientDetails() With {.TransactionCount = "1", .TransactionType = "Q", .Transactions = New Transactions() With {.TextA = "Test"}}
Or a more readable code:
Dim trans As New Transactions
trans.TextA = "Test"
Dim clientDetails As New ClientDetails()
With clientDetails
.TransactionCount = "1"
.TransactionType = "Q"
.Transactions = trans
End With

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

Access deserialize JSON and avoid var declaration

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.

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.

Define String ENUM in VB.Net

I am using Window Application for my project. There is situation where i need to define string enum and using it in my project.
i.e.
Dim PersonalInfo As String = "Personal Info"
Dim Contanct As String = "Personal Contanct"
Public Enum Test
PersonalInfo
Contanct
End Enum
Now i want value of that variable PersonalInfo and Contract as "Personal Info" and "Personal Contanct".
How can i get this value using ENUM? or anyother way to do it.
Thanks in advance...
For non-integer values, Const in a Structure (or Class) can be used instead:
Structure Test
Const PersonalInfo = "Personal Info"
Const Contanct = "Personal Contanct"
End Structure
or in a Module for direct access without the Test. part:
Module Test
Public Const PersonalInfo = "Personal Info"
Public Const Contanct = "Personal Contanct"
End Module
In some cases, the variable name can be used as a value:
Enum Test
Personal_Info
Personal_Contanct
End Enum
Dim PersonalInfo As String = Test.Personal_Info.ToString.Replace("_"c, " "c)
' or in Visual Studio 2015 and newer:
Dim Contanct As String = NameOf(Test.Personal_Contanct).Replace("_"c, " "c)
You could just create a new type
''' <completionlist cref="Test"/>
Class Test
Private Key As String
Public Shared ReadOnly Contact As Test = New Test("Personal Contanct")
Public Shared ReadOnly PersonalInfo As Test = New Test("Personal Info")
Private Sub New(key as String)
Me.Key = key
End Sub
Public Overrides Function ToString() As String
Return Me.Key
End Function
End Class
and when you use it, it kinda looks like an enum:
Sub Main
DoSomething(Test.Contact)
DoSomething(Test.PersonalInfo)
End Sub
Sub DoSomething(test As Test)
Console.WriteLine(test.ToString())
End Sub
output:
Personal Contanct
Personal Info
How about using Tagging. Something like:
Public Enum MyEnum
<StringValue("Personal Contact")>Contact
<StringValue("My PersonalInfo")>PersonalInfo
End Enum
You would have to write the StringValue attribute as:
Public Class StringValueAttribute
Inherits Attribute
Public Property Value As String
Public Sub New(ByVal val As String)
Value = val
End Sub
End Class
To get it out:
Public Function GetEnumByStringValueAttribute(value As String, enumType As Type) As Object
For Each val As [Enum] In [Enum].GetValues(enumType)
Dim fi As FieldInfo = enumType.GetField(val.ToString())
Dim attributes As StringValueAttribute() = DirectCast(fi.GetCustomAttributes(GetType(StringValueAttribute), False), StringValueAttribute())
Dim attr As StringValueAttribute = attributes(0)
If attr.Value = value Then
Return val
End If
Next
Throw New ArgumentException("The value '" & value & "' is not supported.")
End Function
Public Function GetEnumByStringValueAttribute(Of YourEnumType)(value As String) As YourEnumType
Return CType(GetEnumByStringValueAttribute(value, GetType(YourEnumType)), YourEnumType)
End Function
And then a call to get the Enum (using string attribute):
Dim mEnum as MyEnum = GetEnumByStringValueAttribute(Of MyEnum)("Personal Contact")
To get the "Attribute" value out (removed handling 'Nothing' for clarity):
Public Function GetEnumValue(Of YourEnumType)(p As YourEnumType) As String
Return DirectCast(Attribute.GetCustomAttribute(ForValue(p), GetType(StringValueAttribute)), StringValueAttribute).Value
End Function
Private Function ForValue(Of YourEnumType)(p As YourEnumType) As MemberInfo
Return GetType(YourEnumType).GetField([Enum].GetName(GetType(YourEnumType), p))
End Function
And the call to get the string attribute (using Enum):
Dim strValue as String = GetEnumValue(Of MyEnum)(MyEnum.Contact)
How can i get this value using ENUM? or anyother way to do it.
There are three common ways of mapping enum values to strings:
Use a Dictionary(Of YourEnumType, String)
Decorate the enum values with attributes (e.g. DescriptionAttribute) and fetch them with reflection
Use a Switch statement
The first of these options is probably the simplest, in my view.
I know this is an old post put I found a nice solution that worth sharing:
''' <summary>
''' Gives acces to strings paths that are used often in the application
''' </summary>
Public NotInheritable Class Link
Public Const lrAutoSpeed As String = "scVirtualMaster<.lrAutoSpeed>"
Public Const eSimpleStatus As String = "scMachineControl<.eSimpleStatus>"
Public Const xLivebitHMI As String = "scMachineControl<.xLivebitHMI>"
Public Const xChangeCycleActive As String = "scMachineControl<.xChangeCycleActive>"
End Class
Usage:
'Can be anywhere in you applicaiton:
Link.xChangeCycleActive
This prevents unwanted extra coding, it's easy to maintain and I think this minimizes extra processor overhead.
Also visual studio shows the string attributes right after you type "Link"
just like if it is a regular Enum
If all you want to do is display the enums in a list or combo, you can use tagging such as
Private Enum MyEnum
Select_an_option___
__ACCOUNTS__
Invoices0
Review_Invoice
__MEETINGS__
Scheduled_Meetings0
Open_Meeting
Cancelled_Meetings0
Current_Meetings0
End Enum
Then pull the MyEnum into a string and use Replace (or Regex) to replace the tags: "___" with "...", "__" with "**", "_" with " ", and remove trailing numbers. Then repack it up into an array and dump it into a combobox which will look like:
Select an option...
**ACCOUNTS**
Invoices
Review Invoice
**MEETINGS**
Scheduled Meetings
Open Meeting
Cancelled Meetings
Current Meetings
(You can use the numbers to, say, disable a text field for inputting an invoice number or meeting room. In the example, Review Invoice and Open Meeting might be expecting additional input so a text box might be enabled for those selections.)
When you parse the selected combo item, the enumeration will work as expected but you only really need to add a single line of code - the text replacement - to get the combo to look as you wish.
(The explanation is about 10 times as involved as the actual solution!)
This technique from Microsoft - "How to: Determine the String Associated with an Enumeration Value (Visual Basic)" - will be useful in some situations (it didn't help with mine unfortunately though :( ). Microsoft's example:
VB:
Public Enum flavorEnum
salty
sweet
sour
bitter
End Enum
Private Sub TestMethod()
MsgBox("The strings in the flavorEnum are:")
Dim i As String
For Each i In [Enum].GetNames(GetType(flavorEnum))
MsgBox(i)
Next
End Sub