0

So I am using this myMarketNews api: https://mymarketnews.ams.usda.gov/mars-api/getting-started

and I am able to pull the reports list themselves with the following code:

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using RestSharp;
using RestSharp.Authenticators;

namespace MarketNewsJson
{
    internal static class MarketNewsJson
    {
        private static void Main(string[] args)
        {
            var client = new RestClient("https://marsapi.ams.usda.gov")
            {
                Authenticator = new HttpBasicAuthenticator("mars_test_343343", "")
            };

            var request = new RestRequest("services/v1.1/reports", DataFormat.Json);
            var response = client.Get(request);

            var reports = JsonConvert.DeserializeObject<List<Report>>(response.Content);
            foreach (var report in reports)
            {
                Console.WriteLine($"{report.SlugId} {report.SlugName} - {report.ReportTitle}");
            }
        }
    }

    public class Report
    {
        [JsonProperty("slug_id")]
        public string SlugId { get; set; }

        [JsonProperty("slug_name")]
        public string SlugName { get; set; }

        [JsonProperty("report_title")]
        public string ReportTitle { get; set; }

        [JsonProperty("published_date")]
        public string PublishedDate { get; set; }

        [JsonProperty("markets")]
        public List<string> Markets { get; set; }

        [JsonProperty("market_types")]
        public List<string> MarketTypes { get; set; }

        [JsonProperty("offices")]
        public List<string> Offices { get; set; }

        [JsonProperty("sectionNames")]
        public List<string> SectionNames { get; set; }
    }
}

This will pull the report names and their other attributes. However, I also want to be able to pull the JSON information for individual reports themselves. I noticed in the "sorting" tab it shows the properties of an individual report and that for specifying an individual report, it would be something like var request = new RestRequest("services/v1.1/reports/1095", DataFormat.Json); for report 1095 itself instead of /reports for all reports. When I do this, I get an error for deserializing:

Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MarketNewsJson.Report]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

Is this because the format of the JSON for an individual report is different than the format of the reports list itself? If so, will I need a new line to deserialize it for individual reports and just have the other commented out when I want to grab an individual report and its properties?

EDIT: JSON Response

This is what the JSON response is like for my current just pulling report names, id, and title (which works fine):

[
   {
      "slug_id": "1034",
      "slug_name": "MD_DA105",
      "report_title": "Whey - Western & Eastern Europe Report",
      "published_date": "07/30/2020 08:25:01",
      "markets": ["N/A"],
       "market_types": ["Point of Sale"],
       "offices": ["Madison"],
       "sectionNames": []
    }, {
         "slug_id": "1035",
         "slug_name": "MD_DA106",
         "report_title": "Skim Milk Powder - Europe",
         "published_date": "07/30/2020 08:25:01",
          "markets": ["N/A"],
          "market_types": ["Point of Sale"],
          "offices": ["Madison"],
          "sectionNames": []
  }
]

and from the documentation, this is what the individual data for a report is supposed to look like:

{
  "results": [
    {
      "report_begin_date": "2018-01-08",
      "report_end_date": "2018-01-08",
      "published_date": "2018-01-31",
      "office_name": "Madison",
      "office_code": "DA-MD",
      "office_city": "Madison",
      "office_state": "WI",
      "market_location_name": "National Cold Storage",
      "market_location_city": "",
      "market_location_state": "WI",
      "group": null,
      "category": "Hard Products",
      "commodity": "Cheese",
      "market_type": "Cold Storage",
      "market_type_category": "Dairy Market",
      "slug_id": "1095",
      "slug_name": "MD_DA953",
      "community": "Dairy",
      "quality": "N/A",
      "holdings_unit": "LBS",
      "holdings_current_lbs": "96009227",
      "holdings_1stDayMTH_lbs": "96245049",
      "holdings_change_lbs": "-235822",
      "holdings_change_percent": "0.0000",
      "currentMTH_1stDay": "2018-01-01",
      "report_narrative": null,
      "commodity_narrative": null,
      "special_announcement": null
}
  • Error indicates you are deserializing a single object into a List. Look at the JSON and see if you have an array you are deserializing or an object. – Jawad Aug 11 '20 at 02:28
  • Yeah. It sounds like passing the ID returns a single object instead of a collection. So you'll need to deserialize a single object instead of a collection: `JsonConvert.DeserializeObject(response.Content)` – devNull Aug 11 '20 at 02:29
  • @dbc Sorry forgot about that! Just added the example responses – confusion123 Aug 11 '20 at 02:38
  • @devNull So would i basically have that line to use when I want to deserialize a single report itself, and the one I have currently if I just wanted to pull all reports? So it would be: var reports = JsonConvert.DeserializeObject(response.Content) – confusion123 Aug 11 '20 at 02:39
  • Well these two responses return completely different JSON, so you will need different data models to deserialize each. Try generating a second data model for the second response using one of the answers from [How to auto-generate a C# class file from a JSON string](https://stackoverflow.com/q/21611674/3744182), and you should be OK. – dbc Aug 11 '20 at 02:41
  • @dbc that was what I was thinking of but I wasn't sure. So I'd be adding a new class model for the properties of the individual reports itself, that doesn't have to do with my current "Report" class – confusion123 Aug 11 '20 at 02:45
  • Yes that's correct. And you would deserialize to a single `Results` class not a `List`, since the root JSON container is an object, not an array. – dbc Aug 11 '20 at 02:46
  • @dbc Since it is an object this time and not an array, wouldn't I need a new class that is something like:public class Result { public IList Data { get; set; } } If reportAttributes is my new class with the properties for the new JSON. Well that's what i am trying so i can try to print an individual property in a foreach loop as i am doing in the code above but i am getting errors for that. – confusion123 Aug 11 '20 at 03:07
  • Well it would be something like `public partial class RootObject { public List Results { get; set; } }`, see https://app.quicktype.io?share=rPyrzlJngMRFuvDGuHB3. I generated that with one of the answers from [How to auto-generate a C# class file from a JSON string](https://stackoverflow.com/q/21611674/3744182) by the way. – dbc Aug 11 '20 at 04:00

1 Answers1

0

You will have to work with two different classes. One would be for the List and the other would be ReportObject.

For services/v1.1/reports, you will need to use the class you already have in your post. But for the report you get with services/v1.1/reports/1095, you will need to use a different class to deserialize your response to. This is mainly because the response you get is mostly different from the report you get without the ID.

public class ReportResultResponse {
    // This is a List of objects even though it might always be a single element in the json
    [JsonProperty("results")]
    public List<ReportResultForID> Results {get;set;}
}

public class ReportResultForID {
    [JsonProperty("report_begin_date")]
    public DateTime ReportBeginDate {get;set;}
    
    [JsonProperty("report_end_date")]
    public DateTime ReportEndDate {get;set;}
    ...

    [JsonProperty("slug_name")]
    public string SlugName {get;set;}
    ...
}

// Then for the reports you get with ID, you would deserialize like this,
var request = new RestRequest("services/v1.1/reports/1095", DataFormat.Json);
var response = client.Get(request);
var reports = JsonConvert.DeserializeObject<ReportResultResponse>(response.Content);

Jawad
  • 11,028
  • 3
  • 24
  • 37
  • 1
    Ok, that makes sense and is what I was going towards. And if I wanted to print out say just reportBeginDate, I would just do a foreach like: `foreach(ReportResultForID item in reports.Results) { Console.WriteLine(item.ReportBeginDate); } ` Thank you for the help! – confusion123 Aug 11 '20 at 04:09