How to read and process multiple JSON API responses asynchronously? - json

I'm reading JSON responses from the Binance Api, from this link
I need to get some of the data out of it and this is the code I'm using:
Imports System.Net
Imports Newtonsoft.Json
Imports System.Collections.Generic
Public Class Form1
Private wc As New WebClient()
Private wc1 As New WebClient()
Private wc2 As New WebClient()
Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim btc = Await wc.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BTCEUR")
Dim doge = Await wc1.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=DOGEEUR")
Dim bnb = Await wc2.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BNBEUR")
Dim d = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(btc)
Dim d1 = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(doge)
Dim d2 = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(bnb)
Label1.Text = "PRICE " + d("lastPrice")
Label2.Text = "24H CHANGE " + d("priceChange")
Label3.Text = "24H CHANGE % " + d("priceChangePercent")
Label4.Text = "HIGH 24H " + d("highPrice")
Label5.Text = "LOW 24H " + d("lowPrice")
Label6.Text = "PRICE " + d1("lastPrice")
Label7.Text = "24H CHANGE " + d1("priceChange")
Label8.Text = "24H CHANGE % " + d1("priceChangePercent")
Label9.Text = "HIGH 24H " + d1("highPrice")
Label10.Text = "LOW 24H " + d1("lowPrice")
Label11.Text = "PRICE " + d2("lastPrice")
Label12.Text = "24H CHANGE " + d2("priceChange")
Label13.Text = "24H CHANGE % " + d2("priceChangePercent")
Label14.Text = "HIGH 24H " + d2("highPrice")
Label15.Text = "LOW 24H " + d2("lowPrice")
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Timer1.Start()
End Sub
End Class
This code is working perfectly, the Timer.Intrval is set at 1000ms, but after a while I'm getting an exception:
System.NotSupportedException: WebClient does not support concurrent I/O operations
in the line:
Dim bnb = Await wc2.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BNBEUR")
How can I solve it? It doesn't seems wrong cause I'm using 3 different WebClients objects to do that.
Also, how can I just display just 2 decimals after the comma ?

Since you have all async method to call, I suggest to move the API requests to an async method that, when initialized, keeps sending requests to the API - with a delay between calls - until the CancellationToken passed to the method signals that its time to quit.
I'm passing a Progress<T> delegate to the method, which is responsible to update the UI when the Tasks started by the aysnc method return their results.
The delegate of course executes in the UI Thread (here; anyway, the Thread that created and initialized it).
You can run this method from any other method / event handler that can be aysnc. Here, for example, the Click handler of a button. You can also start it from the Form.Load handler. Or whatever else.
I've decide to deserialize the JSON responses to a class model, since some values need to be converted to different types to make sense. As the Date/Time values returned, which are expressed in Unix (milliseconds) notation. So I'm using a custom UnixDateTimeConverter to convert the Date/Time values to DateTimeOffset structures.
Imports System.Net
Imports System.Net.Http
Imports System.Threading
Imports System.Threading.Tasks
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Converters
Private ctsBinance As CancellationTokenSource = Nothing
Private Async Sub SomeButton_Click(sender As Object, e As EventArgs) Handles SomeButton.Click
ctsBinance = New CancellationTokenSource()
Dim progressReport = New Progress(Of BinanceResponseRoot())(AddressOf BinanceProgress)
Try
' Pass the Pogress<T> delegate, the delay in ms and the CancellationToken
Await DownLoadBinanceData(progressReport, 1000, ctsBinance.Token)
Catch tcEx As TaskCanceledException
Console.WriteLine("Tasks canceled")
Finally
ctsBinance.Dispose()
End Try
End Sub
Private Sub BinanceProgress(results As BinanceResponseRoot())
Console.WriteLine("PRICE " & results(0).LastPrice.ToString("N2"))
Console.WriteLine("24H CHANGE " & results(0).PriceChange.ToString("N2"))
Console.WriteLine("24H CHANGE % " & results(0).PriceChangePercent.ToString("N2"))
Console.WriteLine("HIGH 24H " & results(0).HighPrice.ToString("N2"))
Console.WriteLine("LOW 24H " & results(0).LowPrice.ToString("N2"))
Console.WriteLine("PRICE " & results(1).LastPrice.ToString("N2"))
Console.WriteLine("24H CHANGE " & results(1).PriceChange.ToString("N2"))
Console.WriteLine("24H CHANGE % " & results(1).PriceChangePercent.ToString("N2"))
Console.WriteLine("HIGH 24H " & results(1).HighPrice.ToString("N2"))
Console.WriteLine("LOW 24H " & results(1).LowPrice.ToString("N2"))
Console.WriteLine("PRICE " & results(1).LastPrice.ToString("N2"))
Console.WriteLine("24H CHANGE " & results(2).PriceChange.ToString("N2"))
Console.WriteLine("24H CHANGE % " & results(2).PriceChangePercent.ToString("N2"))
Console.WriteLine("HIGH 24H " & results(2).HighPrice.ToString("N2"))
Console.WriteLine("LOW 24H " & results(2).LowPrice.ToString("N2"))
End Sub
To cancel the execution of the Tasks, call the Cancel() method of the CancellationTokenSource. If the Tasks are not canceled before the Form / Window closes, call it when the Form / Window is closing, handling that event.
ctsBinance?.Cancel()
ctsBinance = Nothing
The worker method:
The method keeps running queries to the API in parallel until a cancellation is requested, calling the Cancel() method of the CancellationTokenSource.
I'm using a static HttpClient to send the API requests, since this is more likely its kind of job (no custom initialization, it uses all defaults: you may need to initialize a HttpClientHandler in some contexts, as specific Security Protocols).
All HttpClient.GetAsStringAsync() Tasks are added to a List(Of Task), then all Tasks are executed calling Task.WhenAll().
When all Tasks return, the API responses are deserialized to the BinanceResponseRoot model and the Progress<T> delegate is called to update the UI with the information received.
Private Shared binanceClient As New HttpClient()
Public Async Function DownLoadBinanceData(progress As IProgress(Of BinanceResponseRoot()),
delay As Integer, token As CancellationToken) As Task
While Not token.IsCancellationRequested
Dim tasks As New List(Of Task(Of String))({
binanceClient.GetStringAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BTCEUR"),
binanceClient.GetStringAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=DOGEEUR"),
binanceClient.GetStringAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BNBEUR")
})
Await Task.WhenAll(tasks)
Dim btcEur = JsonConvert.DeserializeObject(Of BinanceResponseRoot)(tasks(0).Result)
Dim dogeEur = JsonConvert.DeserializeObject(Of BinanceResponseRoot)(tasks(1).Result)
Dim bnbEur = JsonConvert.DeserializeObject(Of BinanceResponseRoot)(tasks(2).Result)
progress.Report({btcEur, dogeEur, bnbEur})
Await Task.Delay(delay, token)
End While
End Function
Class Model to convert that JSON data to the corresponding .Net Type values:
Public Class BinanceResponseRoot
Public Property Symbol As String
Public Property PriceChange As Decimal
Public Property PriceChangePercent As Decimal
Public Property WeightedAvgPrice As Decimal
Public Property PrevClosePrice As Decimal
Public Property LastPrice As Decimal
Public Property LastQty As Decimal
Public Property BidPrice As Decimal
Public Property BidQty As Decimal
Public Property AskPrice As Decimal
Public Property AskQty As Decimal
Public Property OpenPrice As Decimal
Public Property HighPrice As Decimal
Public Property LowPrice As Decimal
Public Property Volume As Decimal
Public Property QuoteVolume As Decimal
<JsonConverter(GetType(BinanceDateConverter))>
Public Property OpenTime As DateTimeOffset
<JsonConverter(GetType(BinanceDateConverter))>
Public Property CloseTime As DateTimeOffset
Public Property FirstId As Long
Public Property LastId As Long
Public Property Count As Long
End Class
Friend Class BinanceDateConverter
Inherits UnixDateTimeConverter
Public Overrides Function CanConvert(t As Type) As Boolean
Return t = GetType(Long) OrElse t = GetType(Long?)
End Function
Public Overrides Function ReadJson(reader As JsonReader, t As Type, existingValue As Object, serializer As JsonSerializer) As Object
Dim uxDT As Long? = serializer.Deserialize(Of Long?)(reader)
Return DateTimeOffset.FromUnixTimeMilliseconds(uxDT.Value)
End Function
Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
Dim dtmo = DirectCast(value, DateTimeOffset)
If dtmo <> DateTimeOffset.MinValue Then
serializer.Serialize(writer, CType(DirectCast(value, DateTimeOffset).ToUnixTimeMilliseconds(), ULong))
Else
MyBase.WriteJson(writer, Nothing, serializer)
End If
End Sub
End Class

1000ms is probably too fast, the wc2.DownloadStringTaskAsync task is probably not finished. You can Stop your timer before starting those download tasks and Start it again when the tasks are done:
Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Timer1.Stop
Dim downloadTasks As New List(Of Task(Of String))
Dim btc = wc.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BTCEUR")
Dim doge = wc1.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=DOGEEUR")
Dim bnb = wc2.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/24hr?symbol=BNBEUR")
downloadTasks.Add(btc)
downloadTasks.Add(doge)
downloadTasks.Add(bnb)
Await Task.WhenAll(downloadTasks)
Dim d = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(btc.Result)
Dim d1 = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(doge.Result)
Dim d2 = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(bnb.Result)
Label1.Text = "PRICE " + d("lastPrice")
Label2.Text = "24H CHANGE " + d("priceChange")
Label3.Text = "24H CHANGE % " + d("priceChangePercent")
Label4.Text = "HIGH 24H " + d("highPrice")
Label5.Text = "LOW 24H " + d("lowPrice")
Label6.Text = "PRICE " + d1("lastPrice")
Label7.Text = "24H CHANGE " + d1("priceChange")
Label8.Text = "24H CHANGE % " + d1("priceChangePercent")
Label9.Text = "HIGH 24H " + d1("highPrice")
Label10.Text = "LOW 24H " + d1("lowPrice")
Label11.Text = "PRICE " + d2("lastPrice")
Label12.Text = "24H CHANGE " + d2("priceChange")
Label13.Text = "24H CHANGE % " + d2("priceChangePercent")
Label14.Text = "HIGH 24H " + d2("highPrice")
Label15.Text = "LOW 24H " + d2("lowPrice")
Timer1.Start
End Sub
That way you will be sure the previous download has completed.
You can also check if the WebClient is still busy using the WebClient.IsBusy property before starting another download.
As far as displaying 2 decimals, take a look at Strings.FormatNumber. You can specify a NumDigitsAfterDecimal parameter that indicates
how many places are displayed to the right of the decimal. The default value is -1, which indicates that the computer's regional settings are used.

Related

Internal Server Error 500 when calling a REST service in VB

I'm working with a coworker's code trying to recreate his REST web service in a console application. I'm very new to web services and VB so it's been quite difficult for me. It will work fine on his but I keep getting an Internal Server Error (500). I have looked this up and some suggestions I found were changing "POST" to "GET" but I received "Cannot get content body with this verb-type". I've seen forums about something like this not working from another person's machine. My coworker and I have tried going through it over and over but getting the same results.
This is the Console App
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Net
Imports System.Reflection
Imports System.Text
Imports System.Threading.Tasks
Imports System.Web
Imports System.Web.UI
Imports System.Xml
Module RestConsoleClient
Private req As HttpWebRequest = Nothing
Private res As HttpWebResponse = Nothing
Private responseText As String = ""
Sub Main(args As String())
Dim url As String = ""
Try
If args.Count < 1 OrElse String.IsNullOrEmpty(args(0)) Then
Console.WriteLine("Endpoint Address is required as commandline argument;" _
& vbCrLf & "Copy/Paste one of the endpoint addresses from My.Settings into the Debug commandline arguments section" _
& vbCrLf & vbCrLf & "Press ENTER to start over...")
Console.ReadKey()
Return
Else
url = args(0)
End If
req = CType(WebRequest.Create(url), HttpWebRequest)
req.Method = "POST"
'** The conditions below allowed for generic raw data to be input as text vs. processing input data as a json object.
'** These conditions became redundant with the addition of config for WebContentTypeMapper and corresponding class to accept
'** a post declared with application/json ContentType as generic raw data, and then SUBSEQUENTLY de/serialize the json data as necessary
'If url.Contains("streamweaver") Then
' req.ContentType = "text"
'ElseIf url.Contains("auth") Then
' req.ContentType = "application/json" '; charset=utf-8"
'Else
' 'No other options are yet determined
'End If
req.Timeout = 30000
Dim sJson As String = "{""Name"":""Ashu"",""Age"":""29"",""Exp"":""7 Years"",""Technology"":"".NET""}"
Dim postBytes = Encoding.UTF8.GetBytes(sJson)
req.ContentLength = postBytes.Length
Dim requestStream As Stream = req.GetRequestStream()
requestStream.Write(postBytes, 0, postBytes.Length)
res = CType(req.GetResponse(), HttpWebResponse)
' I retained the basics for response reception even though it was not integral to this test
Dim responseStream As Stream = res.GetResponseStream()
Dim streamReader = New StreamReader(responseStream)
responseText = streamReader.ReadToEnd()
Console.WriteLine("HTTP Response: " & res.StatusCode & " - " & res.StatusDescription.Trim)
Console.WriteLine("[ Response Data: " & responseText.Trim & " ]")
Console.ReadKey()
Catch ex As Exception
Console.WriteLine(ex.Message)
Console.ReadKey()
End Try
End Sub
End Module
This here is the Interface
Imports System.IO
Imports System.ServiceModel
Imports System.ServiceModel.Web
<ServiceContract> _
Public Interface IRestServiceImpl
'<OperationContract> _
'<WebInvoke(Method:="GET", ResponseFormat:=WebMessageFormat.Xml, BodyStyle:=WebMessageBodyStyle.Wrapped, UriTemplate:="xml/{id}")> _
'Function XMLData(id As String) As String
'<OperationContract> _
'<WebInvoke(Method:="GET", ResponseFormat:=WebMessageFormat.Json, BodyStyle:=WebMessageBodyStyle.Wrapped, UriTemplate:="json/{id}")> _
'Function JSONData(id As String) As String
<OperationContract> _
<WebInvoke(Method:="POST", ResponseFormat:=WebMessageFormat.Json, RequestFormat:=WebMessageFormat.Json, BodyStyle:=WebMessageBodyStyle.Bare, UriTemplate:="auth")> _
Function Auth(rData As Stream) As ResponseData
<OperationContract> _
<WebInvoke(Method:="POST", ResponseFormat:=WebMessageFormat.Json, RequestFormat:=WebMessageFormat.Json, BodyStyle:=WebMessageBodyStyle.Bare, UriTemplate:="streamweaver")> _
Function StreamWeaver(sData As Stream) As String
End Interface
And here is the class
Imports System
Imports System.IO
Imports System.Reflection
Imports System.Security.AccessControl
Imports System.Text
Imports Newtonsoft.Json
Public Class RestServiceImpl
Implements IRestServiceImpl
Private filePathOnServer As String = My.Settings.OutputFolder
'Public Function XMLData(id As String) As String Implements IRestServiceImpl.XMLData
' Return "You requested product " & id
'End Function
'Public Function JSONData(id As String) As String Implements IRestServiceImpl.JSONData
' Return "You requested product " & id
'End Function
Public Function Auth(request As Stream) As ResponseData Implements IRestServiceImpl.Auth
Dim streamReader = New StreamReader(request)
Dim requestText As String = streamReader.ReadToEnd()
Dim rData As RequestData = JsonConvert.DeserializeObject(Of RequestData)(requestText)
Dim response = New ResponseData() With { _
.Name = rData.Name, _
.Age = rData.Age, _
.Exp = rData.Exp, _
.Technology = rData.Technology _
}
Dim fileOutput As String = "As of " & DateTime.Now & ", " _
& response.Name.Trim & " is a person who is " _
& response.Age.Trim & " years old, having " _
& response.Exp.Trim & " of experience with " _
& response.Technology.Trim & " technology."
Console.SetError(New StreamWriter("C:\Users\apearson\Documents\Eureka.txt"))
Console.[Error].WriteLine(fileOutput)
Console.[Error].Close()
Dim ctx As WebOperationContext = WebOperationContext.Current
If rData IsNot Nothing Then
ctx.OutgoingResponse.StatusCode = Net.HttpStatusCode.Accepted
Else
ctx.OutgoingResponse.StatusCode = Net.HttpStatusCode.NotAcceptable
End If
Return response
End Function
Public Function StreamWeaver(reqData As Stream) As String Implements IRestServiceImpl.StreamWeaver
StreamWeaver = Nothing
Dim ctx As WebOperationContext = WebOperationContext.Current
If reqData IsNot Nothing Then
ctx.OutgoingResponse.StatusCode = Net.HttpStatusCode.Accepted
Else
ctx.OutgoingResponse.StatusCode = Net.HttpStatusCode.NotAcceptable
End If
Dim streamReader As StreamReader = New StreamReader(reqData)
StreamWeaver = streamReader.ReadToEnd()
Console.SetError(New StreamWriter(filePathOnServer.Trim & "\RestServiceJSONRaw.txt"))
Console.[Error].WriteLine(DateTime.Now & ": " & vbCrLf & StreamWeaver.Trim)
Console.[Error].Close()
Return StreamWeaver
End Function
End Class
If there is anymore information needed, let me know. Again, this is kind of new to me.

Tracking progress of HttpWebRequest and HttpWebResponse

I'm using Sharefile API that sends HTTP requests and gets their respective answers. They are made through URL's and use always the same function. Here it is.
Private Function InvokeShareFileOperation(ByVal requestUrl As String) As JObject
Dim request As HttpWebRequest = WebRequest.Create(requestUrl)
Dim response As HttpWebResponse = request.GetResponse()
Dim reader As StreamReader = New StreamReader(response.GetResponseStream())
Dim json As String = reader.ReadToEnd()
response.Close()
Return JObject.Parse(json)
End Function
As some operations are a bit long, I need to somehow track their progress while they are underway and don't know how to do it. Later I'm intending to use that progress and create a progress bar from it.
(EDIT)
By the way, it's the second code line (below) that takes most time, that is the operation to track.
Dim response As HttpWebResponse = request.GetResponse()
EDIT: I don't think you are going to be able to measure progress in any accurate way here as the bulk of the operation seems to be reliant on the remote server processing the request. GetResponse() handles setting up the DNS, connecting, sending and waiting for the remote server and this is all out of your hands. Reading the response stream is only measurable if the content-length header is returned. Personally I would show progress as 20% initially, 60% when GetResponse returns, and then the last 40% could be shown incrementally if you have the content length before downloading, or done in one go once you have finished reading the response stream.
As its a web request you can find out the content length first and then read the response stream using a buffer instead of ReadToEnd(). This allows you to calculate the progress and fire off notifications while downloading the response.
Dim request As HttpWebRequest = WebRequest.Create(requestUrl)
Using response As HttpWebResponse = request.GetResponse()
Dim contentLength As Long = response.ContentLength
Dim bytesReceived As Long
Dim bufferLength as Integer = 8192
Dim buffer(bufferLength) As Char
Dim sb As New StringBuilder
Using reader As StreamReader = New StreamReader(response.GetResponseStream())
Do
Dim bufferedCount As Integer = reader.Read(buffer, 0, bufferLength)
sb.Append(buffer, 0, bufferedCount)
bytesReceived += bufferedCount
Console.WriteLine(bytesReceived / contentLength * 100 & "%")
Loop While bytesReceived < contentLength
End Using
Return JObject.Parse(sb.ToString)
End Using
Obviously you can substitute the Console.WriteLine with a progress update function or a call to a SignalR hub to update a web page, and you can experiment with the buffer size to see what works best for you.
First we must find out what's slowing down. Request isn't send until GetResponse() is called, so processing by server can take some time. Downloading can also take some time. If response is small (relative to connection speed), you can't do much (you can if server is yours, but we'll focus on client) because you can't get progress from server. If response is large, and you want to track downloading, you can only do it if you have Content-Length header. And to get only headers, server must support HEAD request method. So here is code :
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Threading
Imports Microsoft.VisualBasic
Public Class Form1
Private Function InvokeShareFileOperation(ByVal requestUrl As String) As JObject
HTTPWebRequest_GetResponse.Main(requestUrl)
ProgressBar1.Value = 0
Dim result As String
Do
Try
ProgressBar1.Value = HTTPWebRequest_GetResponse.progress
Catch ex As ArgumentOutOfRangeException
ProgressBar1.Style = ProgressBarStyle.Marquee
End Try
If HTTPWebRequest_GetResponse.done = True Then
result = HTTPWebRequest_GetResponse.response
ProgressBar1.Style = ProgressBarStyle.Continuous
ProgressBar1.Value=100
Debug.WriteLine(result)
Return JObject.Parse(result)
Exit Do
End If
Loop
End Function
End Class
Public Class RequestState
' This class stores the State of the request.
Private BUFFER_SIZE As Integer = 1024
Public requestData As StringBuilder
Public BufferRead() As Byte
Public request As HttpWebRequest
Public response As HttpWebResponse
Public streamResponse As Stream
Public Sub New()
BufferRead = New Byte(BUFFER_SIZE) {}
requestData = New StringBuilder("")
request = Nothing
streamResponse = Nothing
End Sub 'New
End Class 'RequestState
Class HTTPWebRequest_GetResponse
Private BUFFER_SIZE As Integer = 1024
Public Shared response As String
Public Shared done As Boolean = False
Public Shared length As Long = 1
Public Shared progress As Integer
Public Shared myHttpWebRequest As HttpWebRequest
Public Shared myRequestState As New RequestState()
Shared Sub Main(url As String)
Try
Dim headRequest As HttpWebRequest = WebRequest.Create(url)
headRequest.Method = "HEAD"
Dim headResponse As HttpWebResponse = headRequest.GetResponse
length = headResponse.ContentLength
Debug.WriteLine(length)
headResponse.Close()
' Create a HttpWebrequest object to the desired URL.
myHttpWebRequest = WebRequest.Create(url)
' Create an instance of the RequestState and assign the previous myHttpWebRequest
' object to its request field.
myRequestState.request = myHttpWebRequest
'Dim myResponse As New HTTPWebRequest_GetResponse()
' Start the asynchronous request.
Dim result As IAsyncResult = CType(myHttpWebRequest.BeginGetResponse(New AsyncCallback(AddressOf RespCallback), myRequestState), IAsyncResult)
Catch e As WebException
Debug.WriteLine("Main Exception raised!")
Debug.WriteLine("Message: " + e.Message)
Debug.WriteLine("Status: " + e.Status)
Catch e As Exception
Debug.WriteLine("Main Exception raised!")
Debug.WriteLine("Source : " + e.Source)
Debug.WriteLine("Message : " + e.Message)
End Try
End Sub 'Main
Private Shared Sub RespCallback(asynchronousResult As IAsyncResult)
Debug.WriteLine("RespCallBack entered")
Try
' State of request is asynchronous.
Dim myRequestState As RequestState = CType(asynchronousResult.AsyncState, RequestState)
Dim myHttpWebRequest As HttpWebRequest = myRequestState.request
myRequestState.response = CType(myHttpWebRequest.EndGetResponse(asynchronousResult), HttpWebResponse)
' Read the response into a Stream object.
Dim responseStream As Stream = myRequestState.response.GetResponseStream()
myRequestState.streamResponse = responseStream
' Begin the Reading of the contents of the HTML page.
Dim asynchronousInputRead As IAsyncResult = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, New AsyncCallback(AddressOf ReadCallBack), myRequestState)
Return
Catch e As WebException
Debug.WriteLine("RespCallback Exception raised!")
Debug.WriteLine("Message: " + e.Message)
Debug.WriteLine("Status: " + e.Status)
Catch e As Exception
Debug.WriteLine("RespCallback Exception raised!")
Debug.WriteLine("Source : " + e.Source)
Debug.WriteLine("Message : " + e.Message)
End Try
End Sub 'RespCallback
Private Shared Sub ReadCallBack(asyncResult As IAsyncResult)
Debug.WriteLine("ReadCallBack entered")
Try
Dim myRequestState As RequestState = CType(asyncResult.AsyncState, RequestState)
Dim responseStream As Stream = myRequestState.streamResponse
Dim read As Integer = responseStream.EndRead(asyncResult)
' Read the HTML page.
If read > 0 Then
myRequestState.requestData.Append(Encoding.ASCII.GetString(myRequestState.BufferRead, 0, read))
If length = -1 Or length = 0 Then
progress = -1
Else
progress = myRequestState.BufferRead.Length * 100 / length
Debug.WriteLine(progress)
End If
Dim asynchronousResult As IAsyncResult = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, New AsyncCallback(AddressOf ReadCallBack), myRequestState)
Else
If myRequestState.BufferRead.Length > 1 Then
Dim fullResponse As String = myRequestState.requestData.ToString
response = fullResponse.Substring(0, fullResponse.IndexOf("</body>")).Substring(fullResponse.IndexOf(">", fullResponse.IndexOf("<body")) + 2) 'Returns only body
' Release the HttpWebResponse resource.
myRequestState.response.Close()
done = True
Debug.WriteLine(done)
End If
responseStream.Close()
End If
Catch e As WebException
Debug.WriteLine("ReadCallBack Exception raised!")
Debug.WriteLine("Message: " + e.Message)
Debug.WriteLine("Status: " + e.Status)
Catch e As Exception
Debug.WriteLine("ReadCallBack Exception raised!")
Debug.WriteLine("Source : " + e.Source)
Debug.WriteLine("Message : " + e.Message)
End Try
End Sub 'ReadCallBack
End Class 'HttpWebRequest_BeginGetResponse
I took code from http://msdn.microsoft.com/en-us/library/debx8sh9(v=vs.110).aspx and changed it.
EDIT: Code now returns only body and response is closed.
EDIT2: As #Geezer68 said, it's not 100% accurate, but it's OK for showing progress to user.
I'm pretty sure what you want is reader.BaseStream.Length so you can know the length before reading. (At least I did, so I tried) But it threw a NotSupportedException with the message This stream does not support seek operations. So I googled StreamReader + This stream... and found this SO link:
Error “This stream does not support seek operations” in C#
So the short answer is: It is not possible.
Maybe a simple stopwatch is a way to start with ?
Dim timer As System.Diagnostics.Stopwatch = New Stopwatch()
Dim request As HttpWebRequest = WebRequest.Create(requestUrl)
timer.Start()
Dim response As HttpWebResponse = request.GetResponse()
timer.Stop()
Dim reader As StreamReader = New StreamReader(response.GetResponseStream())
Dim json As String = reader.ReadToEnd()
response.Close()
Label1.Text = "Secs:" & timer.Elapsed.ToString()
Here is a link to Microsoft Example
where you set the buffer size and a call back on the response object
https://msdn.microsoft.com/en-us/library/86wf6409%28v=vs.110%29.aspx

Deserializing JSON in Visual basic

Basically, I'm trying to parse the comments from a 4chan thread using the 4chan JSON API. https://github.com/4chan/4chan-API
basically, there is one rich text box called input, and another called post_text_box. What im trying to do is make it so that JSON from a 4chan thread entered in the input text box, and comments are extracted from that JSON and displayed in the output text box
however, whenever I try clicking the Go button nothing happens.
Here is my code so far
Imports System.Web.Script.Serialization
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class Form1
Private Sub start_button_Click(sender As Object, e As EventArgs) Handles start_button.Click
Dim j As Object = New JavaScriptSerializer().Deserialize(Of Post)(input.Text)
post_text_box.Text = j.com
End Sub
End Class
Public Class Rootobject
Public Property posts() As Post
End Class
Public Class Post
Public Property no As Integer
Public Property now As String
Public Property name As String
Public Property com As String
Public Property filename As String
Public Property ext As String
Public Property w As Integer
Public Property h As Integer
Public Property tn_w As Integer
Public Property tn_h As Integer
Public Property tim As Long
Public Property time As Integer
Public Property md5 As String
Public Property fsize As Integer
Public Property resto As Integer
Public Property bumplimit As Integer
Public Property imagelimit As Integer
Public Property replies As Integer
Public Property images As Integer
End Class
Since you're importing Newtonsoft.Json, you can just use the JsonConvert.DeserializeObject<T>(String) method:
Dim exampleJson As String = "{ 'no':'123', 'name':'Some Name', 'com':'This is a comment'}"
Dim post As Post = JsonConvert.DeserializeObject(Of Post)(exampleJson)
Dim com As String = post.com
post_text_box.Text = com
Alternatively, if you don't want to create a class for Post, you can use JsonConvert.DeserializeAnonymousType<T>(String, T):
Dim exampleJson As String = "{ 'no':'123', 'name':'Some Name', 'com':'This is a comment'}"
Dim tempPost = New With {Key .com = ""}
Dim post = JsonConvert.DeserializeAnonymousType(exampleJson, tempPost)
Dim com As String = post.com
post_text_box.Text = com
EDIT: It looks like you're getting an array back from the API:
{
"posts" : [{
"no" : 38161812,
"now" : "11\/19\/13(Tue)15:18",
"name" : "Anonymous",
"com" : ‌​ "testing thread for JSON stuff",
"filename" : "a4c",
"ext" : ".png",
"w" : 386,
"h" : 378,
"tn_w" : 250,
"tn_h" : 244,
"tim" ‌​ : 1384892303386,
"time" : 1384892303,
"md5" : "tig\/aNmBqB+zOZY5upx1Fw==",
"fsize" : 6234,
"‌​resto" : 0,
"bumplimit" : 0,
"imagelimit" : 0,
"replies" : 0,
"images" : 0
}
]
}
In that case, you will need to change the type that is being deserialized to Post():
First, add another small wrapper class:
Public Class PostWrapper
Public posts() As Post
End Class
Then adjust your deserialization code:
Dim json As String = input_box.Text
Dim postWrapper = JsonConvert.DeserializeObject(Of PostWrapper)(json) ' Deserialize array of Post objects
Dim posts = postWrapper.posts
If posts.Length = 1 Then ' or whatever condition you prefer
post_text_box.Text = posts(0).com
End If
Instead of needing to define a class, you can deserialize the JSON into an Object, like this:
Dim json As String = "{""items"":[{""Name"":""John"",""Age"":""20"",""Gender"":""Male""},{""Name"":""Tom"",""Age"":""25"",""Gender"":""Male""},{""Name"":""Sally"",""Age"":""30"",""Gender"":""Female""}]}"
Dim jss = New JavaScriptSerializer()
Dim data = jss.Deserialize(Of Object)(json)
Now, as an example, you could loop through the deserialized JSON and build an HTML table, like this:
Dim sb As New StringBuilder()
sb.Append("<table>" & vbLf & "<thead>" & vbLf & "<tr>" & vbLf)
' Build the header based on the keys of the first data item.
For Each key As String In data("items")(0).Keys
sb.AppendFormat("<th>{0}</th>" & vbLf, key)
Next
sb.Append("</tr>" & vbLf & "</thead>" & vbLf & "<tbody>" & vbLf)
For Each item As Dictionary(Of String, Object) In data("items")
sb.Append("<tr>" & vbLf)
For Each val As String In item.Values
sb.AppendFormat(" <td>{0}</td>" & vbLf, val)
Next
Next
sb.Append("</tr>" & vbLf & "</tbody>" & vbLf & "</table>")
Dim myTable As String = sb.ToString()
Disclaimer: I work with C# on a daily basis and this is a C# example using dynamic that was converted to VB.NET, please forgive me if there are any syntax errors with this.
Note:
First you have to install Newtonsoft.Json on nuget console. Then include following code on top of your code.
Imports Newtonsoft.Json
Step:1 Create class with get & set properties.
Public Class Student
Public Property rno() As String
Get
Return m_rno
End Get
Set(value As String)
m_rno = value
End Set
End Property
Private m_rno As String
Public Property name() As String
Get
Return m_name
End Get
Set(value As String)
m_name = value
End Set
End Property
Private m_name As String
Public Property stdsec() As String
Get
Return m_StdSec
End Get
Set(value As String)
m_StdSec = value
End Set
End Property
Private m_stdsec As String
End Class
Step: 2 Create string as a json format and conver as a json object model.
Dim json As String = "{'rno':'09MCA08','name':'Kannadasan Karuppaiah','stdsec':'MCA'}"
Dim stuObj As Student = JsonConvert.DeserializeObject(Of Student)(json)
Step: 3 Just traverses by object.entity name as follows.
MsgBox(stuObj.rno)
MsgBox(stuObj.name)
MsgBox(stuObj.stdsec)
Also, if you have complex json string. If there is subclasses, arrays, etc. in the json string, you can use this way at below. I tried it and it worked for me. I hope it will useful for you.
I accessed root->simpleforecast->forecastday[]->date->hight->celsius,fahrenheit values etc. in the json string.
Dim tempforecast = New With {Key .forecast = New Object}
Dim sFile As String = SimpleTools.RWFile.ReadFile("c:\\testjson\\test.json")
Dim root = JsonConvert.DeserializeAnonymousType(sFile, tempforecast)
Dim tempsimpleforecast = New With {Key .simpleforecast = New Object}
Dim forecast = jsonConvert.DeserializeAnonymousType(root.forecast.ToString(), tempsimpleforecast)
Dim templstforecastday = New With {Key .forecastday = New Object}
Dim simpleforecast = JsonConvert.DeserializeAnonymousType(forecast.simpleforecast.ToString(), templstforecastday)
Dim lstforecastday = simpleforecast.forecastday
For Each jforecastday In lstforecastday
Dim tempDate = New With {Key .date = New Object, .high = New Object, .low = New Object}
Dim forecastday = JsonConvert.DeserializeAnonymousType(jforecastday.ToString(), tempDate)
Dim tempDateDetail = New With {Key .day = "", .month = "", .year = ""}
Dim fcDateDetail = JsonConvert.DeserializeAnonymousType(forecastday.date.ToString(), tempDateDetail)
Weather_Forcast.ForcastDate = fcDateDetail.day.ToString() + "/" + fcDateDetail.month.ToString() + "/" + fcDateDetail.year.ToString()
Dim temphighDetail = New With {Key .celsius = "", .fahrenheit = ""}
Dim highDetail = JsonConvert.DeserializeAnonymousType(forecastday.high.ToString(), temphighDetail)
Dim templowDetail = New With {Key .celsius = "", .fahrenheit = ""}
Dim lowDetail = JsonConvert.DeserializeAnonymousType(forecastday.low.ToString(), templowDetail)
Weather_Forcast.highCelsius = Decimal.Parse(highDetail.celsius.ToString())
Weather_Forcast.lowCelsius = Decimal.Parse(lowDetail.celsius.ToString())
Weather_Forcast.highFahrenheit = Decimal.Parse(lowDetail.fahrenheit.ToString())
Weather_Forcast.lowFahrenheit = Decimal.Parse(lowDetail.fahrenheit.ToString())
Weather_Forcast09_Result.Add(Weather_Forcast)
Next

How to use http request query string property to filter where clause

I need to filter where clause in my GetProduct function using http request query string property. I have set up my filters in urls. (eg burgers.aspx?filter=burgers'). Burgers is the name of database table category(Where ProductCat = filter). I understand I need to pass parameter to interaction class because it does not handle requests. Please help.
Interaction class:
Public Class Interaction
Inherits System.Web.UI.Page
' New instance of the Sql command object
Private cmdSelect As New SqlCommand
' Instance of the Connection class
Private conIn As New Connection
Region "Menu functions and subs"
' Set up the SQL statement for finding a Product by ProductCat
Private Sub GetProduct(ByVal CatIn As String)
' SQL String
Dim strSelect As String
strSelect = "SELECT * "
strSelect &= " FROM Menu "
strSelect &= " WHERE ProductCat = "
strSelect &= "ORDER BY 'ProductCat'"
' Set up the connection to the datebase
cmdSelect.Connection = conIn.Connect
' Add the SQL string to the connection
cmdSelect.CommandText = strSelect
' Add the parameters to the connection
cmdSelect.Parameters.Add("filter", SqlDbType.NVarChar).Value = CatIn
End Sub
'Function to create list of rows and columns
Public Function ReadProduct(ByVal CatIn As String) As List(Of Dictionary(Of String, Object))
'Declare variable to hold list
Dim ReturnProducts As New List(Of Dictionary(Of String, Object))
Try
Call GetProduct(CatIn)
Dim dbr As SqlDataReader
' Execute the created SQL command from GetProduct and set to the SqlDataReader object
dbr = cmdSelect.ExecuteReader
'Get number of columns in current row
Dim FieldCount = dbr.FieldCount()
Dim ColumnList As New List(Of String)
'Loop through all columns and add to list
For i As Integer = 0 To FieldCount - 1
ColumnList.Add(dbr.GetName(i))
Next
While dbr.Read()
'Declare variable to hold list
Dim ReturnProduct As New Dictionary(Of String, Object)
'Loop through all rows and add to list
For i As Integer = 0 To FieldCount - 1
ReturnProduct.Add(ColumnList(i), dbr.GetValue(i).ToString())
Next
'Add to final list
ReturnProducts.Add(ReturnProduct)
End While
cmdSelect.Parameters.Clear()
'Close connection
dbr.Close()
Catch ex As SqlException
Dim strOut As String
strOut = ex.Message
Console.WriteLine(strOut)
End Try
' Return the Product object
Return ReturnProducts
End Function
Code Behind:
Partial Class Burger
Inherits System.Web.UI.Page
'String Used to build the necessary markup and product information
Dim str As String = ""
''Var used to interact with SQL database
Dim db As New Interaction
' New instance of the Sql command object
Private cmdSelect As New SqlCommand
' Instance of the Connection class
Private conIn As New Connection
Protected Sub printMenuBlock(ByVal productName As String)
'Set up variable storing the product and pull from databse
Dim product = db.ReadProduct(productName)
'Add necessary markup to str variable, with products information within
For i As Integer = 0 To product.Count - 1
str += "<div class='menuItem'>"
'str += " <img alt='Item Picture' class='itemPicture' src='" + product(i).ImagePath.Substring(3).Replace("\", "/") + "' />"
str += " <div class='itemInfo'>"
str += " <h1 class='itemName'>"
str += " " + product(i).Item("ProductName") + "</h1>"
'str += " <h3 class='itemDescription'>"
str += " " + product(i).Item("ProductDescription")
str += " <h1 class ='itemPrice'>"
str += " " + product(i).Item("ProductPrice") + "</h1>"
str += " "
str += " </div>"
str += " </div>"
Next
End Sub
''Uses
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'Dim v = Request.QueryString("filter")
'Response.Write("filter is")
'Response.Write(v)
Dim value = Request.QueryString("filter")
'Get string from printMenuBlock method
printMenuBlock(str)
'Print the str variable in menuPlace div
menuPlace.InnerHtml = str
End Sub
End Class
I need a direction on how to pass the Request.QueryString("filter") to GetProduct function to filter by page according to ProductCategory. Thanks in advance.
Try something like this:
Dim filter = Request.QueryString("filter")
Dim sqlStr = "Select * From menu Where ProductCat = #filter Order By ProductCat"
cmdSelect.Parameters.Add("filter", SqlDbType.NVarChar).Value = filter

save x,y spectrum data in table in ms-access

I want to create a table for LEDs. This table creates information such as name, center wavelength and the spectrum, which itself is data in the format intensity over wavelenth as 2 x n table data.
I am a beginner in access and have currently no clue how to insert this to a table.
I could of course create for each LED a table on its own, but there will be hundreds of these spectrum datas.
Such a complex data structure may be difficult to implement in a database table. An option I propose is to have a set of classes that represent the data. Then you can serialize and deserialize (read and write) the data to a file.
Sample Implementation
Module Module1
Sub Main()
Dim leds = New List(Of LED)()
Dim rnd = New Random()
'create a bunch of LEDs
For i = 1 To 10
Dim led = New LED("LED " & (i + 1).ToString(), rnd.Next(0, i * 100))
For x = 1 To 10
led.Spectrum.Add(New SpectrumInfo(rnd.Next(1, 10), rnd.Next(1000, 10000)))
Next
leds.Add(led)
Next
' write the led data to a file
Using sw As New IO.StreamWriter("LED Data.ledx")
Dim xs = New System.Xml.Serialization.XmlSerializer(leds.GetType())
xs.Serialize(sw, leds)
End Using
'read the led data from a file
Dim leds2 = New List(Of LED)()
Using sr = New System.IO.StreamReader("LED Data.ledx")
Dim xs = New System.Xml.Serialization.XmlSerializer(leds2.GetType())
leds2 = DirectCast(xs.Deserialize(sr), List(Of LED))
End Using
'confirm the two are the same
Console.WriteLine("LEDs and LEDS2 are " & If(leds.SequenceEqual(leds2), "the same", "different"))
' alternate saving using binary serializer
' works in cases where XmlSerializer doesn't
' produces smaller files too
'save the led data
Using fs = New System.IO.FileStream("LED Data.ledb", IO.FileMode.Create)
Dim bf = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
bf.Serialize(fs, leds)
End Using
'read the led data
Dim leds3 = New List(Of LED)()
Using fs = New System.IO.FileStream("LED Data.ledb", IO.FileMode.Open)
Dim bf = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
leds3 = DirectCast(bf.Deserialize(fs), List(Of LED))
End Using
'confirm equality
Console.WriteLine("LEDs and LEDS3 are " & If(leds.SequenceEqual(leds3), "the same", "different"))
Console.WriteLine("LEDs2 and LEDS3 are " & If(leds2.SequenceEqual(leds3), "the same", "different"))
Console.ReadLine()
End Sub
End Module
<Serializable()> _
Public Class LED
Dim _name As String
Dim _cWL As Double
Dim _spectrum As List(Of SpectrumInfo)
Public Sub New()
_name = String.Empty
_cWL = 0
_spectrum = New List(Of SpectrumInfo)()
End Sub
Public Sub New(name As String, cwl As Double, ParamArray spectrum() As SpectrumInfo)
_name = name
_cWL = cwl
_spectrum = New List(Of SpectrumInfo)(spectrum)
End Sub
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
_name = value
End Set
End Property
Public Property CenterWavelength As Double
Get
Return _cWL
End Get
Set(value As Double)
_cWL = value
End Set
End Property
Public ReadOnly Property Spectrum As List(Of SpectrumInfo)
Get
Return _spectrum
End Get
End Property
Public Overrides Function Equals(obj As Object) As Boolean
If Not (TypeOf obj Is LED) Then Return False
Dim l2 = DirectCast(obj, LED)
Return l2._name = _name AndAlso l2._cWL = _cWL AndAlso l2._spectrum.SequenceEqual(_spectrum)
End Function
Public Overrides Function ToString() As String
Return String.Format("{0} [{1}]", _name, _cWL)
End Function
Public Overrides Function GetHashCode() As Integer
Dim result As Integer
For Each spec In _spectrum
result = result Xor spec.GetHashCode()
Next
Return result Xor (_name.GetHashCode() + _cWL.GetHashCode())
End Function
End Class
<Serializable()> _
Public Structure SpectrumInfo
Dim _intensity As Double
Dim _wavelength As Double
Public Sub New(intensity As Double, wavelength As Double)
_intensity = intensity
_wavelength = wavelength
End Sub
Public ReadOnly Property Intensity As Double
Get
Return _intensity
End Get
End Property
Public ReadOnly Property Wavelength As Double
Get
Return _wavelength
End Get
End Property
Public Overrides Function Equals(obj As Object) As Boolean
If TypeOf obj Is SpectrumInfo Then
Dim si = DirectCast(obj, SpectrumInfo)
Return si._wavelength = _wavelength AndAlso si._intensity = _intensity
Else
Return False
End If
End Function
Public Overrides Function ToString() As String
Return String.Format("Intensity: {0}, Wavelength: {1}", _intensity, _wavelength)
End Function
Public Overrides Function GetHashCode() As Integer
Return _intensity.GetHashCode() Xor _wavelength.GetHashCode()
End Function
End Structure
You might look at http://r937.com/relational.html
I think you want:
LED Table
ID
LEDName
CenterWavelength
And then a table for spectra
ID
LedId
Intensisty
WaveLength