IMAbev IMAbev - 3 months ago 7
Vb.net Question

Deserialize Google maps Geocode JSON information

I am trying to deserialize google map geocode information. I am close but I am missing something.

Here is the JSON output

{
"results" : [
{
"address_components" : [
{
"long_name" : "1600",
"short_name" : "1600",
"types" : [ "street_number" ]
},
{
"long_name" : "Amphitheatre Pkwy",
"short_name" : "Amphitheatre Pkwy",
"types" : [ "route" ]
},
{
"long_name" : "Mountain View",
"short_name" : "Mountain View",
"types" : [ "locality", "political" ]
},
{
"long_name" : "Santa Clara County",
"short_name" : "Santa Clara County",
"types" : [ "administrative_area_level_2", "political" ]
},
{
"long_name" : "California",
"short_name" : "CA",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "United States",
"short_name" : "US",
"types" : [ "country", "political" ]
},
{
"long_name" : "94043",
"short_name" : "94043",
"types" : [ "postal_code" ]
}
],
"formatted_address" : "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
"geometry" : {
"location" : {
"lat" : 37.4224764,
"lng" : -122.0842499
},
"location_type" : "ROOFTOP",
"viewport" : {
"northeast" : {
"lat" : 37.4238253802915,
"lng" : -122.0829009197085
},
"southwest" : {
"lat" : 37.4211274197085,
"lng" : -122.0855988802915
}
}
},
"place_id" : "ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
"types" : [ "street_address" ]
}
],
"status" : "OK"
}


Here are my classes:

Public Class AddressComponent

Public Property LongName As String
Public Property ShortName As String
Public Property Types As String()

End Class

Public Class Location

Public Property Lat As Double
Public Property Lng As Double

End Class

Public Class Northeast

Public Property Lat As Double
Public Property Lng As Double

End Class

Public Class Southwest

Public Property Lat As Double
Public Property Lng As Double

End Class

Public Class Viewport

Public Property Northeast As Northeast
Public Property Southwest As Southwest

End Class

Public Class Geometry

Public Property Location As Location
Public Property LocationType As String
Public Property Viewport As Viewport

End Class

Public Class Result

Public Property AddressComponents As AddressComponent()
Public Property FormattedAddress As String
Public Property Geometry As Geometry
Public Property PlaceId As String
Public Property Types As String()

End Class

Public Class GeocodeClass

Public Property Results As Result()
Public Property Status As String

End Class


And here's my code I am have been messing with

Try

Dim url As String = "https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=KEY_OMITTED"
Dim wc As New WebClient()
Dim json = DirectCast(JsonConvert.DeserializeObject(wc.DownloadString(url)), JObject)

Dim formattedaddress = json("results").SelectMany(Function(x) x("address_components")).FirstOrDefault(Function(t) t("types").First().ToString() = "country")
'Dim country = json("results").SelectMany(Function(x) x("address_components")).FirstOrDefault(Function(t) t("types").First().ToString() = "country")
Dim addressinfo As GeocodeClass = JsonConvert.DeserializeObject(Of GeocodeClass)(json)

TextBox2.Text = formattedaddress.ToString

Catch ex As Exception

End Try

End Sub


I am able to get an output from the "country" string. My goal is to make a request and insert the data in to a database. I am close but I am still pretty lost. Can someone nudge me in a direction? JSON is a new concept and so many examples are in C# and unfortunately vb.net is more familiar to me.

Answer

You almost have everything in place to Deserialize it to an object, but you dont need to do any of that pre-processing to the string. You also "fixed up" too many things in the classes one of the robots created for you.

If you look at the json, it clearly shows a property named address_components - when you changed that to AddressComponents those parts will come back as Nothing/null because the properties appear to be missing (they are!). The same applies to long_name to LongName.

Its OK to change class names, but not properties. For that, use the JsonProperty attribute:

<JsonProperty("lat")>
Public Property Latitude As Single

This basically creates an alias so the deserializer can use "lat" and leaves you free to use Latitude.

Also, all the robots are a bit inefficient when it comes to creating the classes for you (even VS). You might have noticed that Location, Northeast and Southwest are identical. This means you can use the same class for all of them. Revised classes note that I left some of the property names alone like "results":

Public Class GeocodeClass
    Public Property results As Result()
    Public Property status As String
End Class

Public Class Result
    <JsonProperty("address_components")>
    Public Property AddressComponents As AddressComponents()
    <JsonProperty("formatted_address")>
    Public Property FormattedAddress As String
    <JsonProperty("Geometry")>
    Public Property geometry As Geometry
    Public Property place_id As String
    Public Property types As String()
End Class

Public Class Geometry
    Public Property location As Location
    Public Property location_type As String
    Public Property viewport As Viewport
End Class

' class for Location, ViewPort.Northeast, ViewPort.southwest
Public Class Location
    <JsonProperty("lat")>
    Public Property Lat As Single
    <JsonProperty("lng")>
    Public Property Lng As Single
End Class

Public Class Viewport
    <JsonProperty("northeast")>
    Public Property Northeast As Location
    <JsonProperty("southwest")>
    Public Property Southwest As Location
End Class

Public Class AddressComponents
    <JsonProperty("long_name")>
    Public Property LongName As String
    <JsonProperty("short_name")>
    Public Property ShortName As String
    Public Property types As String()
End Class

Then to deserialize:

Dim jstr = ....the json from whereever
Dim geoInfo = JsonConvert.DeserializeObject(Of GeocodeClass)(jstr)

' == "California"
Console.WriteLine(geoInfo.results(0).AddressComponents(4).LongName)

That outermost class does not have much info in it, and it creates one more layer to navigate. One way to get rid of it:

Dim geoAddress() As Result            ' a bad Type name
Dim jobj = JObject.Parse(jstr)
If jobj("status").ToString = "OK" Then
    geoAddress = JsonConvert.DeserializeObject(Of Result())(jobj("results").ToString())
End If

' also prints "California"
Console.WriteLine(geoAddress(0).AddressComponents(4).LongName)

If the status (not "Status" in this case) is OK, then it just deserializes the results (not "Results") portion into an array so you no longer have to preface everything with geoInfo. Using the second, you no longer need the GeocodeClass