Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to consume SmartyStreets JSON with JSON.Net... "Cannot deserialize JSON array into type Components"

I'm trying to consume SmartyStreets JSON LiveAddress API and I'm having some difficulties. I will admit that I'm not too familiar with JSON. Anyways, I've tried a few different methods and I usually end up with the error "Cannot deserialize JSON array into type Metadata".

Here is the JSON string:

[{"input_index":0,"candidate_index":0,"delivery_line_1":"1600 Amphitheatre Pkwy","last_line":"Mountain View CA 94043-1351","delivery_point_barcode":"940431351000","components":{"primary_number":"1600","street_name":"Amphitheatre","street_suffix":"Pkwy","city_name":"Mountain View","state_abbreviation":"CA","zipcode":"94043","plus4_code":"1351","delivery_point":"00","delivery_point_check_digit":"0"},"metadata":{"record_type":"S","county_fips":"06085","county_name":"Santa Clara","carrier_route":"C058","congressional_district":"14"},"analysis":{"dpv_match_code":"Y","dpv_footnotes":"AABB","dpv_cmra":"N","dpv_vacant":"N","ews_match":false,"footnotes":"N#"}}]

I used the jsontocsharp webapp to create classes.

Here is the code I'm using:

using (var webClient = new WebClient())
        {
            var json = webClient.DownloadString("url");

            var md = JsonConvert.DeserializeObject<Metadata>(json);
            litTest.Text = md.county_name;
        }

Which then throws the error I mentioned above.

Any assistance would be greatly appreciated.

Thank you, Andrew

like image 650
AJ Tatum Avatar asked Jan 18 '23 08:01

AJ Tatum


2 Answers

I'm a developer at SmartyStreets--thanks for using our service!

The main thing you should understand is that the JSON response is an array of address objects, not just one. This is because an address may be ambiguous, requiring selection/confirmation by the consumer.

So that means you need to tell Json.Net to deserialize the JSON as the top-level address object and then traverse into each address to get the metadata (you were trying to parse the metadata directly, which doesn't work because there's one metadata section for each address in the array returned). That's basically what L.B is doing in his answer, except he's added some extra overhead in order to use dynamic.

Here's an alternate solution that uses the same "DeserializeObject" from your question:

namespace JsonFun
{
    using System;
    using System.Net;
    using Newtonsoft.Json;

    class Program
    {
        private const string Url = "https://api.qualifiedaddress.com/street-address/?street=1600%20Amphitheatre%20Parkway&street2=&city=Mountain%20View&state=CA&zipcode=94043&candidates=10&auth-token=YOUR_AUTH_TOKEN_HERE";

        static void Main()
        {
            using (var webClient = new WebClient())
            {
                var json = webClient.DownloadString(Url);

                var candidate_addresses = JsonConvert.DeserializeObject<CandidateAddress[]>(json);
                foreach (var item in candidate_addresses)
                    Console.WriteLine(item.metadata.county_name);

                Console.ReadLine();
            }
        }
    }

    public class CandidateAddress
    {
        public int input_index { get; set; }
        public int candidate_index { get; set; }
        public string delivery_line_1 { get; set; }
        public string last_line { get; set; }
        public string delivery_point_barcode { get; set; }
        public Components components { get; set; }
        public Metadata metadata { get; set; }
        public Analysis analysis { get; set; }
    }

    public class Components
    {
        public string primary_number { get; set; }
        public string street_name { get; set; }
        public string street_suffix { get; set; }
        public string city_name { get; set; }
        public string state_abbreviation { get; set; }
        public string zipcode { get; set; }
        public string plus4_code { get; set; }
        public string delivery_point { get; set; }
        public string delivery_point_check_digit { get; set; }
    }

    public class Metadata
    {
        public string record_type { get; set; }
        public string county_fips { get; set; }
        public string county_name { get; set; }
        public string carrier_route { get; set; }
        public string congressional_district { get; set; }
        public double latitude { get; set; }
        public double longitude { get; set; }
        public string precision { get; set; }
    }

    public class Analysis
    {
        public string dpv_match_code { get; set; }
        public string dpv_footnotes { get; set; }
        public string dpv_cmra { get; set; }
        public string dpv_vacant { get; set; }
        public bool ews_match { get; set; }
        public string footnotes { get; set; }
    }
}

So, in the end it will depend on whether you want to work with a statically typed response object or a dynamic one. Good luck!

EDIT: Included Latitude/Longitude fields in sample response (newly released).

like image 107
mdwhatcott Avatar answered Feb 03 '23 23:02

mdwhatcott


I prefer to use dynamic object in these cases (No need for creating ugly classes)

such as:

dynamic jsonObj = JsonUtils.JsonObject.GetDynamicJsonObject(json);
foreach(var item in jsonObj)
{
    Console.WriteLine(item.delivery_line_1 + ", " + 
                      item.last_line + ", " +
                      item.metadata.county_name + ", " +
                      item.components.street_name + ", " + 
                      item.components.city_name  );

}

Here is the source for Dynamic Json Object (just copy & paste to your project) and some samples

PS: This is your json string in more readable format

[
    {
        "input_index": 0,
        "candidate_index": 0,
        "delivery_line_1": "1600 Amphitheatre Pkwy",
        "last_line": "Mountain View CA 94043-1351",
        "delivery_point_barcode": "940431351000",
        "components": {
            "primary_number": "1600",
            "street_name": "Amphitheatre",
            "street_suffix": "Pkwy",
            "city_name": "Mountain View",
            "state_abbreviation": "CA",
            "zipcode": "94043",
            "plus4_code": "1351",
            "delivery_point": "00",
            "delivery_point_check_digit": "0"
        },
        "metadata": {
            "record_type": "S",
            "county_fips": "06085",
            "county_name": "Santa Clara",
            "carrier_route": "C058",
            "congressional_district": "14"
        },
        "analysis": {
            "dpv_match_code": "Y",
            "dpv_footnotes": "AABB",
            "dpv_cmra": "N",
            "dpv_vacant": "N",
            "ews_match": false,
            "footnotes": "N#"
        }
    }
]
like image 32
L.B Avatar answered Feb 03 '23 22:02

L.B