Search code examples
jsonvb.netresthttpwebrequest

Create Json in VB.net from string and use it in WebRequest


I'm trying to call openrouteservice api in vb.net and therefore tried to follow the documentation/examples. The example shows below code as working example:

Sub Main(args As String())
    Dim request = TryCast(System.Net.WebRequest.Create("https://api.openrouteservice.org/v2/directions/driving-car/gpx"), System.Net.HttpWebRequest)

    request.Method = "POST"

    request.Accept = "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"
    request.Headers.Add("Authorization", "xxxxMYKEYxxxx")
    request.Headers.Add("Content-Type", "application/json; charset=utf-8")

    Using writer = New System.IO.StreamWriter(request.GetRequestStream())
        Dim byteArray As Byte() = System.Text.Encoding.UTF8.GetBytes({"coordinates:[[8.681495,49.41461],[8.686507,49.41943],[8.687872,49.420318]]"})
        request.ContentLength = byteArray.Length
        writer.Write(byteArray)
        writer.Close()
    End Using
    Dim responseContent As String
    Using response = TryCast(request.GetResponse(), System.Net.HttpWebResponse)
        Using reader = New System.IO.StreamReader(response.GetResponseStream())
            responseContent = reader.ReadToEnd()
            Console.WriteLine(responseContent.ToString())
        End Using
    End Using
End Sub

The example shows that the content of the request body should be the following:

{"coordinates":[[8.681495,49.41461],[8.686507,49.41943],[8.687872,49.420318]]}

But when running the example i get the following error:

'The remote server returned an error: (500) Internal Server Error.'

So i figured that the request fails because of the invalid Json. My question is how to create a JSON resulting in the same as in the example-JSON provided above?


Solution

  • The API is expecting a JSON object, representing an array of arrays of double values, as the content of the HttpRequest, encoded as an UTF-8 string, sent as a byte array.

    This collection can be represented by a List(Of List(Of Double) Property Type of a .Net class:

    ' Note that JavaScriptSrializer might ignore <JsonProperty>,
    ' so use lower case, since the Web API is case-sensitive
    Friend Class MyObject
        <JsonProperty("coordinates")>
        Public Property coordinates As List(Of List(Of Double))
    End Class
    

    After this, we just need to fill the List:

    Dim myobj = New MyObject() With {
        .Coordinates = New List(Of List(Of Double)) From {
            New List(Of Double)({8.681495, 49.41461}),
            New List(Of Double)({8.686507, 49.41943}),
            New List(Of Double)({8.687872, 49.420318})
        }
    }
    

    And serialize it, with either Json.Net (install the NuGet package via NuGet Package Manager) or JavaScriptSerializer (or any other JSON serializer available):

    JavaScriptSerializer requires a Project reference to System.Web.Extension and to import System.Web.Script.Serialization.

    ' Using Json.Net
    Dim Json As String = JsonConvert.SerializeObject(myobj)
    
    ' Or JavaScriptSerializer
    Dim json = New JavaScriptSerializer().Serialize(myobj)
    

    ► The original code has more than one issue:

    • The webRequest.ContentLength is set too late: it needs to be set before the request sent
    • The StreamWriter used to write to the WebRequest Stream will compromise the process: it's not needed and it will be closed before the bytes are written to the Request Stream
    • Content-Type cannot be set using the Headers, we must use WebRequest.ContentType property
    • Added SecurityProtocolType.Tls12 in case this code is used in Windows 7 or a WM (in Windows 7, the default is SecurityProtocolType.Ssl3 Or SecurityProtocolType.Tls, thus TLS1.0, which isn't really used anymore, TLS1.2 is (still) the major player at this time)

    Fixed original code, using JavaScriptSerializer to serialize the request data:

    Replace Imports System.Web.Script.Serialization with Imports Newtonsoft.Json if you use Json.Net.

    Imports System.IO
    Imports System.Text
    Imports System.Web.Script.Serialization
    
    Dim myobj = New MyObject() With {
        .Coordinates = New List(Of List(Of Double)) From {
            New List(Of Double)({8.681495, 49.41461}),
            New List(Of Double)({8.686507, 49.41943}),
            New List(Of Double)({8.687872, 49.420318})
        }
    }
    
    Dim json As String = New JavaScriptSerializer().Serialize(myobj)
    
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
    Dim request = WebRequest.CreateHttp("https://api.openrouteservice.org/v2/directions/driving-car/gpx")
    
    request.Method = "POST"
    request.PreAuthenticate = True
    request.Accept = "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"
    request.Headers.Add("Authorization", "[Your API Key]")
    request.ContentType = "application/json; charset=utf-8"
    
    Dim jsonBytes = Encoding.UTF8.GetBytes(json)
    request.ContentLength = jsonBytes.Length
    Using reqStream = request.GetRequestStream()
        reqStream.Write(jsonBytes, 0, jsonBytes.Length)
    End Using
    
    Dim responseContent As String = String.Empty
    Using response = DirectCast(request.GetResponse(), HttpWebResponse),
        responseStream = response.GetResponseStream(),
        reader = New StreamReader(responseStream)
        responseContent = reader.ReadToEnd()
    End Using