ServiceStack.Text reading json results not working - json

I am just trying to figure out the best way to deserialize a json string returned from a 3rd party api call. I read ServiceStack is fast so want to try it out. No experience and here is what I have done:
Opened Visual Studio 2013
Created new project Windows Forms Application
Installed ServiceStack.Text (based on https://servicestack.net/download)
Added a button (btnView) and textbox (txtOutput)
Add code to btnView_Click event
Private Sub btnView_Click(sender As Object, e As EventArgs) Handles btnView.Click
Me.Cursor = Cursors.WaitCursor
Dim wp As New WebPost 'this allows to pass url and return results
wp.URL = "xxxx"
Dim sJSONRetVal As String = wp.Request(String.Empty, True)
'sJSONRetVal return values looks like the following:
'{"complaints":[{"feedback_type":"abuse","subject":"Sales Agent Position"},{"feedback_type":"abuse","subject":"Sales Agent Position"}],"message":"OK","code":0}
'ServiceStack.Text example
Dim t As SMTP_Complaints = ServiceStack.Text.JsonSerializer.DeserializeFromString(Of SMTP_Complaints)(sJSONRetVal)
'For Each xi As SMTP_Complaints In t
' txtOutput.Text &= xi.mail_from & vbCrLf
'Next
wp = Nothing
txtOutput.Text = t.ToString
Me.Cursor = Cursors.Default
End Sub
Public Class SMTP_Complaints
Dim _feedback_type As String = ""
Dim _subject As String = ""
Public Property feedback_type As String
Get
Return _feedback_type
End Get
Set(value As String)
_feedback_type = value
End Set
End Property
Public Property subject As String
Get
Return _subject
End Get
Set(value As String)
_subject = value
End Set
End Property
End Class
The above doesn't seem to get any data. how would I loop through the data returned and return the data from both instances? Just not sure how I need to set this up to read the json data and then be able to output.

Based on the returned JSON of:
{"complaints":[{"feedback_type":"abuse","subject":"Sales Agent Position"},{"feedback_type":"abuse","subject":"Sales Agent Position"}],"message":"OK","code":0}
You will need two DTOs to deserialise this result.
I have used auto implemented properties here to simplify the complexity of the code. If you use an older version of VB, you'll need to expand these out to include a backing field with get and set method.
Public Class SMTP_Complaint
Public Property feedback_type As String
Public Property subject As String
End Class
Public Class SMTP_ComplaintsResponse
Public Property complaints As SMTP_Complaint()
Public Property message As String
Public Property code As Integer
End Class
You need the SMTP_ComplaintsResponse class because your complaints are wrapped in your JSON response.
Then to deserialise the response:
Dim response = JsonSerializer.DeserializeFromString(Of SMTP_ComplaintsResponse)(sJSONRetVal)
And your complaints are then accessible:
For Each complaint As var In response.complaints
Console.WriteLine("Type: {0}, Subject {1}", complaint.feedback_type, complaint.subject)
Next

Related

Deserialize and serialize Json data in Winform

I have a winform with a combobox and some textboxes. I get the Json data via REST API and deserialize it via Json.NET and a class file.
The JsonHelper
Imports Newtonsoft.Json
Public Module JsonHelper
Public Function FromClass(Of T)(data As T, Optional isEmptyToNull As Boolean = False, Optional jsonSettings As JsonSerializerSettings = Nothing) As String
Dim response As String = String.Empty
If Not EqualityComparer(Of T).Default.Equals(data, Nothing) Then
response = JsonConvert.SerializeObject(data, jsonSettings)
End If
Return If(isEmptyToNull, (If(response = "{}", "null", response)), response)
End Function
Public Function ToClass(Of T)(data As String, Optional jsonSettings As JsonSerializerSettings = Nothing) As T
Dim response = Nothing
If Not String.IsNullOrEmpty(data) Then
response = If(jsonSettings Is Nothing,
JsonConvert.DeserializeObject(Of T)(data),
JsonConvert.DeserializeObject(Of T)(data, jsonSettings))
End If
Return response
End Function
End Module
Imports Newtonsoft.Json
Namespace Models
Public Class Header
<JsonProperty("Name")>
Public Property Name As String
<JsonProperty("DisplayAt")>
Public Property DisplayAt As String
End Class
The Class file
Public Class DataSource
<JsonProperty("Id")>
Public Property Id As String
<JsonProperty("Name")>
Public Property Name As String
<JsonProperty("Headers")>
Public Property Headers As Header()
<JsonProperty("Rows")>
Public Property Rows As String()()
<JsonProperty("TotalRows")>
Public Property TotalRows As Integer
<JsonProperty("LastUpdated")>
Public Property LastUpdated As DateTime
<JsonProperty("CompanyId")>
Public Property CompanyId As Integer
End Class
Public Class Category
<JsonProperty("DataSource")>
Public Property DataSource As DataSource
End Class
End Namespace
The combobox is populated with data from the Json file and via bindingsource the textboxes are connected to the combobox. So if you select another row, the values of the textboxes change.
The bindingsource
Dim bindingSource As BindingSource = New BindingSource()
bindingSource.DataSource = dt
bindingSource.Sort = "Weergave DESC"
ListBox1.DataSource = bindingSource
ListBox1.DisplayMember = "Weergave"
ListBox1.ValueMember = "Id"
'ListBox1.Sorted = True
txtWeergave.DataBindings.Clear()
txtWeergave.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Weergave", True, DataSourceUpdateMode.OnPropertyChanged))
txtProjectnaam.DataBindings.Clear()
txtProjectnaam.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Projectnaam", True, DataSourceUpdateMode.OnPropertyChanged))
txtProjectnummer.DataBindings.Clear()
txtProjectnummer.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Projectnummer", True, DataSourceUpdateMode.OnPropertyChanged))
txtServicemonteur.DataBindings.Clear()
txtServicemonteur.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Servicemonteur", True, DataSourceUpdateMode.OnPropertyChanged))
I wan't to make some changes to the data and update the database via a PUT request.
Therefore I made the following routine:
Private Sub PutData()
Dim request As WebRequest = WebRequest.Create("my url")
request.ContentType = "application/json"
request.Method = "PUT"
' Get the response.
Dim response As HttpWebResponse = CType(request.GetResponse(), HttpWebResponse)
' Get the stream containing content returned by the server.
Dim dataStream As Stream = response.GetResponseStream()
' Open the stream using a StreamReader for easy access.
Dim reader As New StreamReader(dataStream)
' Read the content.
Dim responseFromServer As String = reader.ReadToEnd()
'Dim JObject As Object
Dim rawJson As String = responseFromServer
Dim dataSource As String = JsonHelper.FromClass(Of Category)(rawJson).DataSource
End Sub
The following things are not clear for me.
If I make changes in the textbox, the changes are also made in the combobox because they have been connected with eachother via a bindingsource. But does the values also change in the Class file?
How can I make changes to the values of the textboxes and update the datasource via a PUT request?
It's not completely clear becacuse some things are missing from your question, such as your binding configuration, but..
Generally I wouldn't expect things to be that "when I change the textbox the combo will change". Let's take some artificial example of a Person class with Name and Ethnicity. Name is a string and would be bound to a textbox. Ethnicity is a fixed list of values, and I would expect a combo to be bound up such that the combo's datasource, displaymember and valuememeber related to the columns/properties of a list of ethnicities, and then the combo's selectedvalue be bound to the person's ethnicity setting. This way changing the selected item in the combo edits their ethnicity to one of the other values in the fixed list of ethnicities. Changing the value displayed in the name box doesn't change anything about the ethnicity. If you have a list of multiple Person that the bindingsource is managing, then you can navigate the bindingsource to show different people and edit them. It's probably easiest to see if you also add a datagridview, albeit temporarily, bound to the same bindingsource that persons are bound through; changing the current row of the grid navigates the bindingsource and the textbox/combobox will change to show the details for the new person
The PUT request is easier to answer; you formulate a block of json for the server to work on by re-serializing your edited class to JSON, and then send it as the body of the put request
request.Method = "PUT"
'formulate a body
Dim postData = JsonHelper.FromClass(editedPerson)
Dim encoding As New ASCIIEncoding() 'or whatever
Dim byte1 As Byte() = encoding.GetBytes(postData)
request.ContentType = "application/json"
request.ContentLength = byte1.Length
Dim reqStream = request.GetRequestStream()
reqStream.Write(byte1, 0, byte1.Length)
reqStream.Close()
' Get the response.
Dim response As HttpWebResponse = CType(request.GetResponse(), HttpWebResponse)
By the way, in C# at least, you can right click your project name and choose Add REST Client, give it the URL of the swagger/openapi descriptor of your web service and it will make all the client classes for you which can simplify interaction a lot. Though it's a C# only thing (it's based on autorest which I don't think supports VB), C# and VB are the same thing internally, so you can add a C# project alongside your VB one purely for purposes of creating a rest client, and then import a reference to the c# project into your VB one

Class to JSON VB.Net

So I have a class which I am serializing to Json. All goes well, until this nested class, which gives me an System.NullReferenceException = {"Object reference not set to an instance of an object."}. When writing the code, intelisense recognizes the nested class, but obviously I'm missing a declaration somewhere.
Root class:
Public Class RootObject
Private _metadata As List(Of Metadata)
Public Property metadata() As List(Of Metadata)
Get
Return _metadata
End Get
Set(ByVal value As List(Of Metadata))
_metadata = value
End Set
End Property
Private _test_gl As List(Of TestGl)
Public Property test_gl() As List(Of TestGl)
Get
Return _test_gl
End Get
Set(ByVal value As List(Of TestGl))
_test_gl = value
End Set
End Property
End Class
Here is the TestGl class definition:
Public Class TestGl
Private _ref_key_3 As String
<JsonProperty("ref-key-3")> _
Public Property ref_key_3() As String
Get
Return _ref_key_3
End Get
Set(ByVal value As String)
_ref_key_3 = value
End Set
End Property
Private _currency_amount As CurrencyAmount
<JsonProperty("currency-amount")> _
Public Property currency_amount() As CurrencyAmount
Get
Return _currency_amount
End Get
Set(ByVal value As CurrencyAmount)
_currency_amount = value
End Set
End Property
End Class
And finally the CurrencyAmount class:
Public Class CurrencyAmount
Private _currency As String
<JsonProperty("currency")> _
Public Property currency() As String
Get
Return _currency
End Get
Set(ByVal value As String)
_currency = value
End Set
End Property
Private _amount As String
<JsonProperty("amount")> _
Public Property amount() As String
Get
Return _amount
End Get
Set(ByVal value As String)
_amount = value
End Set
End Property
End Class
Here follow the code of filling up the object with data:
Dim Root As RootObject
Dim Meta_Data As New List(Of Metadata)()
Dim Test_Gl As New List(Of TestGl)()
Root = New RootObject
Root.metadata = New List(Of Metadata)()
Root.test_gl = New List(Of TestGl)
Meta_Data = Root.metadata
Test_Gl = Root.test_gl
And here I assign values to it:
Test_Gl.Add(New AccountGl)
Test_Gl(ItemNo).ref_key_3 = "test"
Test_Gl(ItemNo).currency_amount.currency = "EUR"
Test_Gl(ItemNo).currency_amount.amount = "100"
The line where currency_amount.currency gets assigned, it goes wrong and I'm looking at it for several hours already. I don't see it.
Any input would be highly appreciated.
The properties are written in full as I need to work on this project in VS2008.
I suspect somewhere you need to initialize _currency_amount to a new instance of CurrencyAmount I don't see new CurrencyAmount anywhere.
I suspect that you don't really want to allow the currency_amount property to be set, otherwise you should have set it in your sample assignment code at the bottom. If this is the case, then you probably shouldn't even have a Set member defined for TestGl (it should be ReadOnly, which affects only currency_amount and not _currency_amount.currency). Instead you should create a default instance of CurrencyAmount and assign it to that field during the construction of TestGl. That could be as simple as changing your declaration of _currency_amount to:
Private _currency_amount As New CurrencyAmount
Alternatively, and this may be the solution you need to use with a JSON serializable object, you keep the Set member definition, and just add a line to your test code to initialize currency_amount before using it:
Test_Gl(ItemNo).currency_amount = new CurrencyAmount

Azure Mobile Service Casting JSON to Type in VB.Net

Hi there I have been messing with Azure Mobile Services recently and after solving one problem I have come up against another one. I have a stored procedure held on my Azure SQL db which is then invoked by a custom API on my Mobile service.
In my client I can call the API to return a JSON token (or array):
Dim result As Linq.JToken = Await App.subtlesoftClient.InvokeApiAsync("test1",
System.Net.Http.HttpMethod.Get, Nothing)
I then convert this into a json string:
Dim jString As String = result.ToString
Which gives me:
{[
{
"id": 2,
"message": "This is another test message cos everything is almost almost great but what can you do eh",
"messageDate": "2015-05-10T00:00:00Z"
}
]}
What I can't figure out is how to take the result and pass it into my class:
Public Class test1
Private _ID As Integer
Private _MessageDate As Date
Private _Message As String
<JsonProperty(PropertyName:="id")>
Public Property ID As Integer
Get
Return _ID
End Get
Set(value As Integer)
_ID = value
End Set
End Property
<JsonProperty(PropertyName:="message")>
Public Property Message As String
Get
Return _Message
End Get
Set(value As String)
_Message = value
End Set
End Property
<JsonProperty(PropertyName:="messageDate")>
Public Property MessageDate As Date
Get
Return _MessageDate
End Get
Set(value As Date)
_MessageDate = value
End Set
End Property
End Class
I have tried:
Dim myArray = result.Children(Of Linq.JProperty)().
FirstOrDefault(Function(x) x.Name = "").Value
To try and put it into an array I can enumerate through. This causes errors probably because I am not passing a name into the function as my json does not appear to have a name.
Ultimately the return json will be multiple objects so I want to be able to pass them into a MobileServiceCollection(Of MyType), and I don't know how. The documentation on this seems pretty scant.
Well thanks for giving me some pointers anyway seems it was very easy in the end, isn't it always when you figure it out:
Dim result = Await App.subtlesoftClient.InvokeApiAsync("test1", System.Net.Http.HttpMethod.Get, Nothing)
Dim jString As String = result.ToString
Dim myTest = JsonConvert.DeserializeObject(Of List(Of test1))(jString)
This works fine, seems the square brackets on the JSON string indicate its is an array.

Google finance record Json Parsing vb.net

I am creating vb.net application. I am getting Json data from google finance. I am facing a problem in parsing. The problem is that :
I will give an example (not about google)
This is the class
Public Class MyModel
Dim m_tes As String
Dim m_client_list As String
Public Property type() As String
Get
Return m_tes
End Get
Set(ByVal value As String)
m_tes = value
End Set
End Property
Public Property client_list() As String
Get
Return m_client_list
End Get
Set(ByVal value As String)
m_client_list = value
End Set
End Property
End Class
and this is the JSON Deserializer
Dim deserializedProduct As MyModel = JsonConvert.DeserializeObject(Of MyModel)(JSON)
MsgBox(deserializedProduct.type)
MsgBox(deserializedProduct.client_list)
If I get one record Json data ,It works fine
like
dim JSON = {"type":"newModel","client_list":"Joe"}
The output of the msgbox is
newModel
Joe
The problem is that if I get a list of Json
I need a way to split this list likeh the following:
Json = {"type":"clientlist","client_list":"client 1"},{"type":"clientlist","client_list":"client 1"}
I haven't worked a whole lot with JSON, but I think you can pass a collection into the deserializer to get a back a collection of the deserialized objects.
In other words:
Dim deserializedProduct As List(Of MyModel) = JsonConvert.DeserializeObject(Of List(Of MyModel))(JSON)
The above code is similar to what you posted, except that is uses List<T> instead of a single instance of your MyModel object, and for the type used with the DeserializeObject you give it a List(Of ModelType).
Note that your MsgBox(deserializedProduct.type) and MsgBox(deserializedProduct.client_list) won't show you the results in a List<T> (you'll get the type name instead) - you'll need to loop through the list. Something like this:
For Each Dim model As MyModel In deserializedProduct
MsgBox(model.type)
MsgBox(model.client_list)
Next

Parse Simple JSON with VB.NET

I have a simple JSON string that I am trying to parse with Visual Studio Express 2010 using native .net 4.0 instead of NewtonSoft. The json data that I am trying to parse looks like the following.
"{"token_type":"Bearer",""expires_in":3599,"access_token":"VxwK6YWYj6paqyMK2D2r4uDl34qg"}"
I can get the following code to run without error but when I try to dump the contents of the object I don't have any in the list. Here is the class I created.
Public Class AuthToken
Public Property token_type As String
Get
Return m_token_type
End Get
Set(ByVal value As String)
m_token_type = value
End Set
End Property
Private m_token_type As String
Public Property expires_in() As Integer
Get
Return m_expires_in
End Get
Set(ByVal value As Integer)
m_expires_in = value
End Set
End Property
Private m_expires_in As String
Public Property access_token As String
Get
Return m_access_token
End Get
Set(ByVal value As String)
m_access_token = value
End Set
End Property
Private m_access_token As String
End Class
My feeling is that my problem is in my class but I am not sure. So after looking for hours on this site and others Trouble parsing Json into .net Object I have put together the following code to parse the information and dump it to a RichTextBox just to see what it is.
Dim sr As New StreamReader(req.GetResponse.GetResponseStream)
Dim authtoken As New List(Of AuthToken)()
Dim ms As New MemoryStream(System.Text.Encoding.Unicode.GetBytes(sr.ReadToEnd))
Dim serializer As New System.Runtime.Serialization.Json.DataContractJsonSerializer(authtoken.GetType)
authtoken = DirectCast(serializer.ReadObject(ms), List(Of AuthToken))
ms.Close()
ms.Dispose()
sr.Close()
sr.Dispose()
For Each token In authtoken
rtbResponse.AppendText("Token: " & token.access_token & " Expires in: " & token.expires_in)
Next
So is my class created incorrectly? Is the data from the memorystream just not getting into the authtoken object because the class doesn't match the contents of the json data as it is deserialized?
If I am using the "DataContractSerializer" do I need to have data contract "stuff" in my class?
Any help is GREATLY appreciated.
The data is not getting into the authtoken variable because you don't have a list of auth tokens there - only one object. So if you get those JSON objects one at the time get rid of your lists and do it like that:
Dim sr As New StreamReader(req.GetResponse.GetResponseStream)
Dim authtoken As AuthToken
Dim ms As New MemoryStream(System.Text.Encoding.Unicode.GetBytes(sr.ReadToEnd))
Dim serializer As New System.Runtime.Serialization.Json.DataContractJsonSerializer(GetType(AuthToken))
authtoken = DirectCast(serializer.ReadObject(ms), AuthToken)
ms.Close()
ms.Dispose()
sr.Close()
sr.Dispose()
rtbResponse.AppendText("Token: " & authtoken.access_token & " Expires in: " & authtoken.expires_in)