1

I am trying to access specific properties from my object via Google Books API. The content is deserialized into two POCOs to access the nested object. I am stuck on is accessing the properties of volumeInfo to gather info like the title, author, images, etc.

//POCOs
public class GBAModel1
{
    public List<GBAModel2> items { get; set; }
}
public class GBAModel2
{
    public string id { get; set; }
    public string selfLink { get; set; }
    public Object volumeInfo { get; set; }

}
//REPOSITORY
public async Task<List<GBAModel2>> GetBooksFromApi()
{
    string testUrl = "https://www.googleapis.com/books/v1/volumes?q=jujutsu+kaisen+isbn=%229781974733767%22+inauthor=Gege";

    using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(testUrl))
    {
        if (response.IsSuccessStatusCode)
        {
            GBAModel1 results = await response.Content.ReadFromJsonAsync<GBAModel1>();

            return results.items;
        } else
        {
            throw new Exception(response.ReasonPhrase);
        }
    }
}
//CONTROLLER
[HttpGet("/booksApi")]
public async Task<IActionResult> GetBooks()
{
    var testResult= await _bookAction.GetBooksFromApi();
    var testAccess = testResult[0].volumeInfo;
    return Ok(testAccess);
}
//EXAMPLE JSON
{
  "kind": "books#volumes",
  "totalItems": 1,
  "items": [
    {
      "kind": "books#volume",
      "id": "cltJEAAAQBAJ",
      "etag": "cWfbxxKUP7w",
      "selfLink": "https://www.googleapis.com/books/v1/volumes/cltJEAAAQBAJ",
      "volumeInfo": {
        "title": "Jujutsu Kaisen, Vol. 17",
        "subtitle": "Perfect Preparation",
        "authors": [
          "Gege Akutami"
        ],
        "publisher": "VIZ Media LLC",
        "publishedDate": "2022-08-16",
        "description": "Hunted down by Okkotsu and on the brink of death, Itadori recalls a troubling family scene from his past. But why is the former form of Noritoshi Kamo there? As the sorcerers begin to take action toward suppressing the lethal culling game, Maki pays the Zen’in clan a visit... -- VIZ Media",
        "industryIdentifiers": [
          {
            "type": "ISBN_13",
            "identifier": "9781974733767"
          }
        ],
        "imageLinks": {
          "smallThumbnail": "http://books.google.com/books/content?id=cltJEAAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api",
          "thumbnail": "http://books.google.com/books/content?id=cltJEAAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
        },
        "language": "en",
      },
    }
  ]
}

I tried using a dot property like var testAccess = testResult[0].volumeInfo.title but gave me a CS1061:

'object' does not contain a definition for 'title' and no accessible extension method 'title' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?).

And also tried getting the title through methods:

var testAccess = testResult[0].volumeInfo;
var try2 = testAccess .GetType()
          .GetProperty("title")
          .GetValue(this, null);
return Ok(try2);

...but error was

Object reference not set to an instance of an object.

How can I properly access this object?

2
  • 3
    You would typically use a actual class for your volumeInfo, that contains properties like title, author, images, etc. object should in general be avoided since it does make accessing any properties much more difficult.
    – JonasH
    Commented Apr 26 at 10:56
  • 3
    If you cannot use a specific DTO class (maybe because the transferred structure is not stable) you can deserialize to JObject (Newtonsoft) or the equivalent for System.Text.Json or (but I would not recommend , maybe for quick POCing) dynamic.
    – Fildor
    Commented Apr 26 at 14:02

1 Answer 1

1

In C#, the most idiomatic way of addressing problems such as these is to define DTOs to contain the desired properties:

public class VolumeInfo
{
    public string title { get; set; }
    public string subtitle { get; set; }
    public List<string> authors { get; set; }
    public string publisher { get; set; }
    public string publishedDate { get; set; }
    public string description { get; set; }
    public List<IndustryIdentifier> industryIdentifiers { get; set; }
    public ImageLinks imageLinks { get; set; }
    public string language { get; set; }
}

and then update the volumeInfo property of GBAModel2:

public class GBAModel2
{
    public string id { get; set; }
    public string selfLink { get; set; }
    public VolumeInfo volumeInfo { get; set; }
}

Then you can write testResult.items[0].volumeInfo.title.

If you don't want to create all these DTOs, you can also work directly with the JSON AST.

2
  • Is this practice preferred even when creating 5-6 dtos? I wasn't sure if it would have a huge draw on my application
    – Juan21Guns
    Commented Apr 26 at 21:07
  • 1
    @Juan21Guns As implied by my leading sentence, this is the usual way things are done in C#. If you follow the link, you can read about alternatives, and the trade-offs involved. Commented Apr 27 at 7:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.