Download Files From Web Server - vb.net

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.

Related

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

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

Using classes for JSON serialization

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

Serialization of a class cause error

Windows 10 universal app. VB
I have the class below which I want to save to file and read from file, Saving works but when I attempt to load I get the error.
Message=Error in line 1 position 249. Element 'http://schemas.datacontract.org/2004/07/KoW_Universal_v2:MagicItem' contains data of the 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfKeyValueOfstringMagicItemyoeMIiQz' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'ArrayOfKeyValueOfstringMagicItemyoeMIiQz' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.
Source=System.Private.DataContractSerialization
Imports System.Runtime.Serialization
Imports Windows.Storage
Public Class MagicItem
Property MagicItemName As String
Property MagicItemCost As Integer
Property MagicItemDescripyion As String
Property LimitToHero As Boolean
Property LimitToSpecialRule As String 'key to the special rule
End Class
Public Class clsMagicItems
Private MagicItems As New Dictionary(Of String, MagicItem)
Async Sub SaveMagicItems()
' StorageFile File = Await openPicker.PickSingleFileAsync();
Dim myfile As StorageFile
myfile = Await ApplicationData.Current.LocalFolder.CreateFileAsync("MagicItems.dat", CreationCollisionOption.ReplaceExisting)
Dim KnownTypeList As New List(Of Type)
KnownTypeList.Add(GetType(clsMagicItems))
KnownTypeList.Add(GetType(MagicItem))
Dim r As Streams.IRandomAccessStream
r = Await myfile.OpenAsync(FileAccessMode.ReadWrite)
Using outStream As Streams.IOutputStream = r.GetOutputStreamAt(0)
Dim serializer As New DataContractSerializer(GetType(clsMagicItems), KnownTypeList)
serializer.WriteObject(outStream.AsStreamForWrite(), MagicItems)
Await outStream.FlushAsync()
outStream.Dispose()
r.Dispose()
End Using
End Sub
Async Sub LoadMagicItems()
Try
Dim myfile As Stream
Dim KnownTypeList As New List(Of Type)
KnownTypeList.Add(GetType(clsMagicItems))
KnownTypeList.Add(GetType(MagicItem))
Dim serializer As New DataContractSerializer(GetType(clsMagicItems), KnownTypeList)
myfile = Await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("MagicItems.dat")
'LINE BELOW RAISE ERROR
MagicItems = serializer.ReadObject(myfile)
Catch
'failed to load the file
End Try
End Sub
End Class
Fixed it, should have had the following code
KnownTypeList.Add(GetType(Dictionary(Of String, MagicItem)))
...
Dim serializer As New DataContractSerializer(GetType(Dictionary(Of String, MagicItem)), KnownTypeList)

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.

XmlSerializer returns nothing in my Windows 8 Store App

I have successfully deserialized my xml file using XmlSerializer in .Net but trying to serialize that data back to an xml file is becoming frustrating. When I try to serialize my classes I only get the root tag of the xml with no child elements. How can I serialize all my objects to get the correct xml with data? I have seen somewhere where someone suggested to add the classes to be serialized in a collection and then serialize that collection but I can't wrap my head around that or is there a simpler way of doing it? Any help is appreciated! Here is my code:
Public Shared Function SerializeXml() As Byte()
Dim serializer As New XmlSerializer(GetType(Data))
Dim nameSpaces As XmlSerializerNamespaces = New XmlSerializerNamespaces()
Dim mStream As New MemoryStream()
Dim result As Byte()
Dim target As New Data()
nameSpaces.Add(String.Empty, String.Empty)
serializer.Serialize(mStream, target, nameSpaces)
result = mStream.ToArray()
Return result
And here is a generic sample of the xml with attributes:
<?xml version"1.0">
<RootTag>
<ChildTag Label="Label1" Value="Value1"/>
<ChildTag Label="Label2" Value="Value2"/>
</RootTag>
Edit: Here is my Data class:
Imports System.Xml.Serialization
<XmlRoot("DATA", [Namespace]:="", IsNullable:=False)>
Public Class Data
Inherits Model
<XmlElement("CONFIGURATION")>
Public Property Configuration() As DataConfiguration
Get
Return Me._Configuration
End Get
Set(value As DataConfiguration)
Me._Configuration = value
End Set
End Property
Private _Configuration As DataConfiguration
<XmlElement("FIELD")>
Public Property Field() As Field
Get
Return Me._Field
End Get
Set(value As Field)
Me._Field = value
End Set
End Property
Private _Field As Field
<XmlElement("LIST")>
Public Property ListRoot() As List(Of ListRoot)
Get
Return Me._ListRoot
End Get
Set(value As List(Of ListRoot))
Me._ListRoot = value
End Set
End Property
Private _ListRoot As List(Of ListRoot)
End Class
This is your issue here, <XmlRoot("DATA", [Namespace]:="", IsNullable:=False)>. The IsNullable property, when set to false, will omit the XML for items if they are equal to nothing. If you set the IsNullable to True, then it will emit a tag like this <ListRoot xsi:nil = "true" />. In your code example, since you just created a new Data class like this Dim target As New Data(), all of the members are Nothing by default. Since you've set the IsNullable = False, you should only see that root tag, and that would be a valid serialization of the data.