2

I am trying to learn how to integrate the NOAA API ( https://www.ncdc.noaa.gov/cdo-web/webservices/v2#gettingStarted)

by using a method similar to that outlined here: https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/console-webapiclient

to deserialize weather data associated with a specific location

I have a Result.cs object

public class Result
{
    public DateTime Date { get; set; }
    public string DataType { get; set; }
    public string Station { get; set; }
    public string Attributes { get; set; }
    public int Value { get; set; }
}

which I try to serialize and write to the console to test:

private static async Task ProcessRespositories()
{
    var serializer = new DataContractJsonSerializer(typeof(List<Result>));
    //token set from https://www.ncdc.noaa.gov/cdo-web/token
    string token = "myToken";
    var client = new HttpClient();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Add("token", token);
    //url taken from https://www.ncdc.noaa.gov/cdo-web/webservices/v2#data for daily summaries
    string url = "https://www.ncdc.noaa.gov/cdo-web/api/v2/data?datasetid=GHCND&locationid=ZIP:28801&startdate=2010-05-01&enddate=2010-05-01"
    //this is null
    var streamTask = client.GetStreamAsync(url);
    var repositories = serializer.ReadObject(await streamTask) as List<Result>;

    foreach (var repo in repositories)
        Console.WriteLine(repo.Value);
}

the result I get in the stream from that url seems empty. I'm just learning but unsure of my error here.

coolhand
  • 1,876
  • 5
  • 25
  • 46
  • 1
    What do you mean by "the stream url is null"? What specifically goes wrong when you debug this? – David Jan 17 '18 at 17:06
  • It runs to completion but does not return any data, leading to a repositories count of zero – coolhand Jan 17 '18 at 17:16

1 Answers1

3

The payload that you get out of that response looks like this:

{"metadata":{"resultset":{"offset":1,"count":8,"limit":25}},"results":[{"date":"2010-05-01T00:00:00","datatype":"PRCP","station":"GHCND:US1NCBC0005","attributes":",,N,","value":0},{"date":"2010-05-01T00:00:00","datatype":"SNOW","station":"GHCND:US1NCBC0005","attributes":",,N,","value":0}]}

The type that you give the DataContractSerializer need to match that payload. You're missing a topcontainer. Also the names of fields are case sensitive. I used an DataMember attribute to adjust for the correct casing. Last but not least, the datetime format is not by default supported. For now I changed that to a string type. You might want to see if https://stackoverflow.com/a/9347678/578411 can work for you or switch to Json.NET

Here are the Model classes

public class Noaa
{
    public MetaData metadata {get;set;}
    public List<Result> results;
}

public class MetaData 
{
    public ResultSet resultset {get;set;}
}

public class ResultSet
{
   public int offset{get;set;}
   public int count{get;set;}
   public int limit{get;set;}
}

[DataContract]
public class Result
{
    [DataMember(Name="date")]
    public string Date { get; set; } // now a string!
    [DataMember(Name="datatype")]
    public string DataType { get; set; }
    [DataMember(Name="station")]
    public string Station { get; set; }
    [DataMember(Name="attributes")]
    public string Attributes { get; set; }
    [DataMember(Name="value")]
    public int Value { get; set; }
}

And your serializer would need to change to use the container type:

var serializer = new DataContractJsonSerializer(typeof(Noaa));

// other code the same

// call the serializer
var repositories = (Noaa) serializer.ReadObject(await streamTask);

// respositories is now a Noaa type, 
// its member results holds your list.
foreach (var repo in repositories.results)
            Console.WriteLine(repo.Value);

If you want to examine the payload instead of handing it to the serializer you can use a StreamReader like so:

var sw= new StreamReader(await streamTask);
Console.WriteLine(sw.ReadToEnd());

use a Chrome plugin Postman or use a webproxy like Fiddler

rene
  • 41,474
  • 78
  • 114
  • 152
  • This works and returns the appropriate objects in the repositories. However, it doesn't allow me to loop through the repositories as previous. How would the foreach loop need to be modified? Error is: Noaa does not contain a public definition for 'GetEnumerator' – coolhand Jan 17 '18 at 19:16
  • @coolhand yep. missed that as I commented it during testing. I added it to my example code, see my edit. – rene Jan 17 '18 at 19:19
  • Yep, that was it! I wasn't using the '.results' in my iteration – coolhand Jan 17 '18 at 19:31
  • how did you examine the payload in this case? I would like to do something similar to understand how to build my model to fit the stream payload when I test different urls – coolhand Jan 23 '18 at 02:23
  • I was thrown for a loop when Postman was throwing errors due to the US govt shutdown affecting the Noaa site, but now I'm getting results. Thank you so much! – coolhand Jan 24 '18 at 18:24
  • @SkippyVonDrake you can delete your own comments, no moderator is needed for that. – rene Oct 26 '21 at 16:59