My question relates to the question: JSON import to Excel
I understand most of what this post is saying, but I don't understand how to put it together in an excel module or class module. Would someone please help me to understand how I can assemble this beast to achieve my purpose?
EDIT 29-5-14 # 7.44AM -
Here is the try which I have done so far:
Installed as ClassModule syncWebRequest
'BEGIN CLASS syncWebRequest
Private Const REQUEST_COMPLETE = 4
Private m_xmlhttp As Object
Private m_response As String
Private Sub Class_Initialize()
Set m_xmlhttp = CreateObject("Microsoft.XMLHTTP")
End Sub
Private Sub Class_Terminate()
Set m_xmlhttp = Nothing
End Sub
Property Get Response() As String
Response = m_response
End Property
Property Get Status() As Long
Status = m_xmlhttp.Status
End Property
Public Sub AjaxPost(Url As String, Optional postData As String = "")
m_xmlhttp.Open "POST", Url, False
m_xmlhttp.setRequestHeader "Content-type", "application/x-www-form-urlencoded"
m_xmlhttp.setRequestHeader "Content-length", Len(postData)
m_xmlhttp.setRequestHeader "Connection", "close"
m_xmlhttp.send (postData)
If m_xmlhttp.readyState = REQUEST_COMPLETE Then
m_response = m_xmlhttp.responseText
End If
End Sub
Public Sub AjaxGet(Url As String)
m_xmlhttp.Open "GET", Url, False
m_xmlhttp.setRequestHeader "Connection", "close"
m_xmlhttp.send
If m_xmlhttp.readyState = REQUEST_COMPLETE Then
m_response = m_xmlhttp.responseText
End If
End Sub
'END CLASS syncWebRequest
Installed as Module Test
Sub Test()
Dim request As New syncWebRequest
request.AjaxGet "http://crimson/php/SiteEnquiryAjax.php?action=aisFeed&brandCode=s&siteID=0625?format=json"
Dim json As String
json = request.Response
End Sub
I'm used to working with normal modules, but have not had to work with a class module before. I am therefore trying to understand this and translate it to meet my requirements, but forgive me if I am making it a chunkier job than one would expect.
I'm trying to query data from a local webservice that returns JSON. The webservice returns data based on an ID (e.g. 0656) in the form of:
{"total":"1","results":[{"brandName":"ABC","brandingName":"Cat","siteCategory":"Production","siteName":"Scrubbed","streetAddress":"ABC RD, SUBURB, VIC 3000","siteState":"VIC","phoneNumber":"03 0909 0909","mobileNumber":"0409 090 909","faxNumber":"03 9090 9090","applicationServer":"CAT0656ABC001","dateOpened":"1979-08-21","siteNotice":"","lastContact":"2014-05-19 04:36:31",}]}
My objective is to put this in a spreadsheet where the user can enter the sites ID (e.g. 0656) and the spreadsheet will then be able to pull and respond with data as exampled above.
EDIT 29-5-14 # 8.42AM -
Updated Module Test - my intention here was to test the parser exampled in the linked question, however when I now run the macro I receive an error. My understanding is that this may be the missing piece of what I'm trying to achieve. I have now commented out the added items to stop error occurring.
Sub Test()
Dim request As New syncWebRequest
request.AjaxGet "http://crimson/php/SiteEnquiryAjax.php?action=aisFeed&brandCode=s&siteID=0625"
Dim json As String
json = request.Response
'Set clients = parser.Parse(request.Response)
'For Each client In clients
'Name = client("Name")
'State = client("siteState")
'street = client("Address")("Street")
'suburb = client("Address")("Suburb")
'city = client("Address")("City")
'Next
End Sub
EDIT 29-5-14 # 9.40AM -
To put it another way... I want to turn this: HTTP Result of "url/SiteEnquiryAjax.php?action=aisFeed&brandCode=s&siteID=0656"
{"total":"1","results":[{"brandName":"ABC","brandingName":"Cat","siteCategory":"Production","siteName":"Scrubbed","streetAddress":"ABC RD, SUBURB, VIC 3000","siteState":"VIC","phoneNumber":"03 0909 0909","mobileNumber":"0409 090 909","faxNumber":"03 9090 9090","applicationServer":"CAT0656ABC001","dateOpened":"1979-08-21","siteNotice":"","lastContact":"2014-05-19 04:36:31",}]}
Into this: Excel Output
Without knowing the exact details of the error you are receiving, I've used vba-json before and I think you're not instantiating the parser before using it:
Dim parser As New JSONLib
Set clients = parser.parse(Request.Response)
As far as the design of the project goes, I think you've done well to separate the Request as a Class and then making the request from a Module. I ran into many similar problems accessing Salesforce from Excel and created a library that I think may help: Excel-REST. It follows a similar idea, but separates it into three classes: Client, Request, and Response. Here's an example:
Sub RetrieveJSONSimple
Dim Client As New RestClient
Dim Response As RestResponse
Set Response = Client.GetJSON("url...")
If Response.Status = Ok Then
' Response.Data is parsed json
For Each Client in Response.Data
Name = Client("Name")
State = Client("siteState")
Street = Client("Address")("Street")
Suburb = Client("Address")("Suburb")
City = Client("Address")("City")
Next Client
End If
End Sub
Sub RetrieveJSONAdvanced
Dim Client As New RestClient
Client.BaseUrl = "http://crimson/php/"
' Can also setup authentication with HTTP Basic, OAuth, and others
Dim Request As New RestRequest
Request.Resource = "SiteEnquiryAjax.php?action=aisFeed&brandCode=s&siteID={SiteId}"
Request.AddUrlSegment "SiteId", "0625"
' GET and json are default, but can be set
Request.Method = httpGET
Request.Format = json
Dim Response As RestResponse
Set Response = Client.Execute(Request)
' Process as before
End Sub
Related
Need help in solving error in HTTP GET request [closed]
I have the following code in VB.NET framework which should pull the data in json format and add it to a list view control using GET request. However I am getting following error:
System.Threading.Tasks.Task.FromResult(System.Net.Http.HttpResponseMessage)
Dim client = New HttpClient()
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{Subscription-Key}")
Dim uri = "https://example.com/" & SearchKey
Dim response = client.GetAsync(uri)
lstMer.Items.Add(response.ToString)
Initially I wrote the following code in VBA and it was working fine:
Dim jsonText as String
Set req = New MSXML2.ServerXMLHTTP60
key_id = "{Subscription-Key}"
key_header_name = "Subscription-Key-Id"
liveURL = "https://example.com/" & SearchKey
req.Open "GET", liveURL, False
req.setRequestHeader {Subscription-Key-Id}, {Subscription-Key}
req.setRequestHeader "Host", "site Address"
req.send
jsonText = req.responseText
The requirement is to get the headers from URL and fill a list view.
I think I understand. You don't get an error
System.Threading.Tasks.Task.FromResult(System.Net.Http.HttpResponseMessage)
rather you get that as a result of response.ToString(). This is because you are calling client.GetAsync(uri) directly, which returns a Task.FromResult<TResult>. However, it is meant to be run async, and there are some convenient keywords for achieving this, namely Async / Await. If you await the task, instead of returning a Task.FromResult<TResult>, it will return a TResult. So change your code to do just that
Private Async Sub Foo()
Dim client = New HttpClient()
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{Subscription-Key}")
Dim uri = "https://example.com/" & SearchKey
Dim response = Await client.GetAsync(uri)
lstMer.Items.Add(response.ToString)
End Sub
See HttpClient.GetAsync and about what it returns Task.FromResult
I research regarding retrieving or displaying emails from gmail without using outlook and it led me here on using Gmail API. I'm confused because I don't have idea on how to solve my problem.
Here's the code:
Attribute VB_Name = "Gmail"
' Setup client and authenticator (cached between requests)
Private pGmailClient As WebClient
Private Property Get GmailClient() As WebClient
If pGmailClient Is Nothing Then
' Create client with base url that is appended to all requests
Set pGmailClient = New WebClient
pGmailClient.BaseUrl = "https://www.googleapis.com/gmail/v1/"
' Use the pre-made GoogleAuthenticator found in authenticators/ folder
' - Automatically uses Google's OAuth approach including login screen
' - Get API client id and secret from https://console.developers.google.com/
' - https://github.com/timhall/Excel-REST/wiki/Google-APIs for more info
Dim Auth As New GoogleAuthenticator
Auth.Setup CStr(Credentials.Values("Google")("id")), CStr(Credentials.Values("Google")("secret"))
Auth.AddScope "https://www.googleapis.com/auth/gmail.readonly"
Auth.Login
Set pGmailClient.Authenticator = Auth
End If
Set GmailClient = pGmailClient
End Property
' Load messages for inbox
Function LoadInbox() As Collection
Set LoadInbox = New Collection
' Create inbox request with userId and querystring for inbox label
Dim Request As New WebRequest
Request.Resource = "users/{userId}/messages"
Request.AddUrlSegment "userId", "me"
Request.AddQuerystringParam "q", "label:inbox"
Dim Response As WebResponse
Set Response = GmailClient.Execute(Request)
If Response.StatusCode = WebStatusCode.Ok Then
Dim MessageInfo As Dictionary
Dim Message As Dictionary
For Each MessageInfo In Response.Data("messages")
' Load full messages for each id
Set Message = LoadMessage(MessageInfo("id"))
If Not Message Is Nothing Then
LoadInbox.Add Message
End If
Next MessageInfo
End If
End Function
' Load message details
Function LoadMessage(MessageId As String) As Dictionary
Dim Request As New WebRequest
Request.Resource = "users/{userId}/messages/{messageId}"
Request.AddUrlSegment "userId", "me"
Request.AddUrlSegment "messageId", MessageId
Dim Response As WebResponse
Set Response = GmailClient.Execute(Request)
If Response.StatusCode = WebStatusCode.Ok Then
Set LoadMessage = New Dictionary
' Pull out relevant parts of message (from, to, and subject from headers)
LoadMessage.Add "snippet", Response.Data("snippet")
Dim Header As Dictionary
For Each Header In Response.Data("payload")("headers")
Select Case Header("name")
Case "From"
LoadMessage.Add "from", Header("value")
Case "To"
LoadMessage.Add "to", Header("value")
Case "Subject"
LoadMessage.Add "subject", Header("value")
End Select
Next Header
End If
End Function
Sub Test()
Dim Message As Dictionary
For Each Message In LoadInbox
Debug.Print "From: " & Message("from") & ", Subject: " & Message("subject")
Debug.Print Message("snippet") & vbNewLine
Next Message
End Sub
I'm using the blank file from this GitHub Link that enables this code above. I was stuck on this error:
Still I don't have the idea what's the output of this API because it's my first time using this one. I have my id and secret already from Gmail API i just removed it from the code. I guess it was really hard if I don't have someone I can do brainstorming regarding on this matter.
Hoping someone have encountered this problem or anyone is interested. Thanks.
I am new to class programming in vba.
Here is my first attempt using teaching of
:Asynchronous HTTP POST Request in MS Access
I am using word vba.
Here is my CXMLHTTPHandler.cls
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CXMLHTTPHandler"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Dim m_xmlHttp As MSXML2.XMLHTTP
Public Sub Initialize(ByRef xmlHttpRequest As MSXML2.XMLHTTP)
Set m_xmlHttp = xmlHttpRequest
End Sub
Sub OnReadyStateChange()
Attribute OnReadyStateChange.VB_UserMemId = 0
Debug.Print m_xmlHttp.readyState
If m_xmlHttp.readyState = 4 Then
If m_xmlHttp.Status = 200 Then
msgbox m_xmlHttp.responseText
Else
msgbox "Something Went Wrong"
End If
End If
End Sub
Here is my standard module
Option Explicit
Public xmlHttpRequest As MSXML2.XMLHTTP
Function sasynchreq(url As String)
On Error GoTo FailedState
If Not xmlHttpRequest Is Nothing Then Set xmlHttpRequest = Nothing
Dim MyXmlHttpHandler As CXMLHTTPHandler
Set xmlHttpRequest = New MSXML2.XMLHTTP
'Create an instance of the wrapper class.
Set MyXmlHttpHandler = New CXMLHTTPHandler
MyXmlHttpHandler.Initialize xmlHttpRequest
'Assign the wrapper class object to onreadystatechange.
xmlHttpRequest.OnReadyStateChange = MyXmlHttpHandler
'Get the page stuff asynchronously.
xmlHttpRequest.Open "GET", url, True
xmlHttpRequest.send ""
Exit Function
FailedState:
msgbox Err.Number & ": " & Err.Description
End Function
Sub test()
Dim url As String, do_something As String
url = "http://httpbin.org/html"
do_something = sasynchreq(url)
'do somethign with do_something
End Sub
Everything works fine. What if I want to assign httprequest to some variable for example in test sub.?
Sounds like you're after the Property statements. In your case, it would be Property Get (https://msdn.microsoft.com/en-us/library/office/gg264197.aspx).
In your class module, put this:
Public Property Get HttpRequest() As MSXML2.XMLHTTP
Set HttpRequest = m_xmlHttp
End Property
This now gives your Module access to the class variable, like so:
Dim rq as MSXML2.XMLHTTP
set rq = MyXmlHttpHandler.HttpRequest
I think you can expect the ultimate payload predicated on the Send method be present by the time you exit your function. If so, your function signature should look something like:
Function sasynchreq(url As String) As String
and in the body of the function, there should be a:
xmlHttpRequest.send ""
sasynchreq = xmlHttpRequest.responseXML.xml
or something like that. Then do_something would be available right away for use.
Long story short, if there are no synchronicity issues, the way it's currently written, your function is not returning anything back to do_something. Whatever it is you're returning, the function signature and local variable types inside sub test should match. If you are returning an object, you will need to use the Set keyword in both instances.
I am working on a program and uploads a shipping manifest to a the shippers website. When I try to upload, I get a nondescript error back from their server, and when checking with the shipper, they tell me that "there is an issue with the SSL" I am using.
I've spent quite a bit of time piecing together code that, from what I seem to understand, is supposed to work, but I'm not making any progress. As far as I know everything else is fine with the upload, but there is a problem with my SSL certificate
If I understand what this code is supposed to do correctly, I should get a certificate from the shippers website, which allows certification to my program for a space of time during which I can upload the data. I'm really not sure that this is what my code is doing at all, but the only code examples I have seen show it something like this.
Here's my code with the URLs changed:
'This references a custom class that compiles the manifest I'm going to upload
Dim StringToUpload As String = Compile_Manifest(MyDate, UseTestDB)
Dim webClient As New System.Net.WebClient
webClient.Credentials = System.Net.CredentialCache.DefaultCredentials
'From what I understand,
'this is supposed to set up properties used in next section of code
System.Net.ServicePointManager.SecurityProtocol = Net.SecurityProtocolType.Ssl3
System.Net.ServicePointManager.ServerCertificateValidationCallback = _
AddressOf AcceptAllCertifications
'I can see that this reaches the server,
'but I don't know how it relates to the next section of code
'that actually uploads the manifest
Dim ServerRequest As System.Net.WebRequest = _
System.Net.WebRequest.Create("https://www.certify.some-shippper.com:443/somefolder")
Dim ServerResponse As System.Net.WebResponse
ServerResponse = ServerRequest.GetResponse()
ServerResponse.Close()
'This code works for the upload of the manifest,
'and it seems the above code is unrelated and does not use a SSL certificate.
'When this code runs I get the same error back from the shippers server,
'indicating an issue with my SSL, with or without the two sections of code above.
Dim StrResult As String = ""
Dim WrappedString As String = TransmitPLD.WrapPldFile(StringToUpload)
'This references a custom class that wraps the data to upload
'in information from the shipper.
Dim ByesToUpload As Byte() = _
System.Web.HttpUtility.UrlEncodeToBytes(WrappedString, _
System.Text.ASCIIEncoding.ASCII)
Dim Result As Byte() = _
webClient.UploadData("https://www.certify.some-shippper.com:443/somefolder", _
ByesToUpload)
StrResult = System.Web.HttpUtility.UrlDecode(Result, _
System.Text.ASCIIEncoding.ASCII)
MessageBox.Show(StrResult)
So it turns out I went about it the wrong way. I needed to upload my data through System.Net.WebRequest and it takes care of the certificates for me. Not implementing all the parts of the code I needed, it didn't handle the retrieval of the shipper's certificate.
In case anyone else gets confused about the matter like I did, here's my working code for anyone to see, adapt and use. My resource for fixing the code (and by that I mean starting from scratch) was the MSDN page for the WebRequest class, and it has code examples much the same as what I have below in C++, C#, and VB.NET and here is the link.
First there are some global variables that need to be set and class that needs to be created for to store the upload response:
' This is set in the function that Upload function
' and uploads the data in the ReadCallback sub
Private Shared WrappedString As String
' This is used to wait for the callback in the Upload function
Private Shared allDone As New Threading.ManualResetEvent(False)
Friend Class RequestState
' This class stores the request state of the request.
Public request As Net.WebRequest
Public Sub New()
request = Nothing
End Sub ' New
End Class ' RequestState
Then there is a sub needed for the upload part web request which will be called further below in the upload function:
Private Shared Sub ReadCallback(asynchronousResult As IAsyncResult)
Try
Dim myRequestState As RequestState = CType(asynchronousResult.AsyncState, RequestState)
Dim myWebRequest As Net.WebRequest = myRequestState.request
' End the request.
Dim streamResponse As IO.Stream = myWebRequest.EndGetRequestStream(asynchronousResult)
' Convert the string into a byte array.
Dim byteArray As Byte() = System.Text.Encoding.ASCII.GetBytes(WrappedString)
' Write the data to the stream.
streamResponse.Write(byteArray, 0, byteArray.Length)
streamResponse.Close()
' Allow the main thread to resume.
allDone.Set()
Catch ex As Exception
Throw New Exception("Error in " & Reflection.MethodBase.GetCurrentMethod.Name.ToString & " **" & ex.Message, ex)
End Try
End Sub ' ReadCallback
Finally, this is the function that should be called to upload the data, which uses all the code above:
Public Shared Function Upload(ByVal MyDate As Date) As String
Dim StrResult As String = ""
UploadSucess = False
Try
' This is my code that builds the manifest that I want to upload
Dim StringToUpload As String = Compile_PLD200(MyDate)
WrappedString = TransmitPLD.WrapPldFile(StringToUpload)
Dim myWebRequest As Net.WebRequest
myWebRequest = Net.WebRequest.Create("https://www.some.website.com:443/someplace")
' Create an instance of the RequestState and assign
' myWebRequest to it's request field.
Dim myRequestState As New RequestState()
myRequestState.request = myWebRequest
myWebRequest.ContentType = "multipart/mixed; boundary=BOUNDARY"
myRequestState.request.Method = "POST"
' Start the asynchronous 'BeginGetRequestStream' method call.
Dim r As IAsyncResult = CType(myWebRequest.BeginGetRequestStream(AddressOf ReadCallback, myRequestState), IAsyncResult)
' Pause the current thread until the async operation completes.
allDone.WaitOne()
' Send the Post and get the response.
Dim myWebResponse As Net.WebResponse = myWebRequest.GetResponse()
Dim streamResponse As IO.Stream = myWebResponse.GetResponseStream()
Dim streamRead As New IO.StreamReader(streamResponse)
Dim readBuff(256) As [Char]
Dim count As Integer = streamRead.Read(readBuff, 0, 256)
While count > 0
Dim outputData As New [String](readBuff, 0, count)
Console.WriteLine(outputData)
count = streamRead.Read(readBuff, 0, 256)
StrResult += outputData
End While
' Close the Stream Object.
streamResponse.Close()
streamRead.Close()
' Release the HttpWebResponse Resource.
myWebResponse.Close()
Catch ex As Exception
Throw New Exception("Error in " & Reflection.MethodBase.GetCurrentMethod.Name.ToString & " **" & ex.Message, ex)
End Try
Return StrResult
End Function ' Upload
Again here is the MSDN page for the WebRequest class which has a code example too.
Hope this helps anyone who was stuck like I was. And any criticisms as to the implementation of the code are welcome. This just happen to do what I want, I can't say it is the most efficient implementation.
I have nearly completed a basic OneDrive interface, and am able to handle creating and writing folders, files, and so forth. The last element to get working is to update a file's date/time stamp to match that of the local (originating) file.
When I drop a file via the browser interface, the file's date/time stamp shows correctly in the view. This is reflected in the "client_updated_time" when I read the file's properties later in my application. Clear enough.
However, I cannot find any way to update this field programmatically from within my application. I am using the following code, to no avail. I have a valid _accessToken value, a valid fileId for the new file, and the call results always indicates success.
The "name" and "updated_time" elements are just in there to see if anything happens, and the file will indeed rename if I mangle the fileName variable a bit. I didn't expect the "updated_time" to updated, but it seems to me that the "client_updated_time" element should work.
Using fiddler, it appears that the browser-based interface (java?) opens a session, sends the file over, and then in the close-session call uses a header entry labelled "X-Last-Modified-ISO8601" to set the file's date-time stamp. However, using the REST interface, I cannot find any examples of this. The documentation for setting file properties mentions renaming only (which works in this code).
Any feedback on how to accomplish setting "client_updated_time" with REST calls would be much appreciated!
Here's the relevant code:
Private _liveURL As String = "https://apis.live.net/v5.0/"
Private Sub AddAuthorizationHeader(hc As HttpClient, authorization As String)
If Len(authorization) > 0 Then
hc.DefaultRequestHeaders.Authorization = New Headers.AuthenticationHeaderValue("Bearer", authorization)
End If
End Sub
Public Function WebPUT(uri As String, contentType As String, authorization As String, data As String) As String
Dim response As String = String.Empty
Try
Dim hc As New HttpClient()
Dim content As New Http.StringContent(data, System.Text.Encoding.UTF8, contentType)
AddAuthorizationHeader(hc, authorization)
Using r = hc.PutAsync(uri, content).Result
response = r.Content.ReadAsStringAsync.Result
End Using
Catch ex As Exception
' fake it
response = "{""error"": {""code"": ""invalid_request"", ""message"": """ + ex.Message + """}}"
End Try
Return response
End Function
Private Function UpdateFileDateTime(fileId As String, fileName As String, fileDt As String) As Boolean
Dim response As Boolean = False
Dim wr As String = WebHelper.WebPUT(_liveURL + fileId, "application/json", _accessToken, "{ ""name"": """ + fileName + """, ""updated_time"": """ + fileDt + """, ""client_updated_time"": """ + fileDt + """ }")
'... parse wr for response
Return response
End Function
Based on the file object reference, that field is read-only via the REST API: http://msdn.microsoft.com/en-us/library/dn631834.aspx