在C#中如何正确访问Google Books API返回的嵌套对象
问题描述
尝试通过Google Books API获取图书的特定属性,已将返回的JSON反序列化为两个POCO类,但无法访问volumeInfo的嵌套属性(如标题、作者、图片链接等)。
现有POCO定义:
//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); }
API返回的示例JSON:
//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" } } ] }
尝试使用点语法var testAccess = testResult[0].volumeInfo.title时,出现错误:
'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?).
尝试用反射获取标题时:
var testAccess = testResult[0].volumeInfo; var try2 = testAccess.GetType() .GetProperty("title") .GetValue(this, null); return Ok(try2);
出现错误:
Object reference not set to an instance of an object.
解决方案
1. 核心问题原因
GBAModel2中的volumeInfo被定义为Object类型,C#编译器无法识别它的具体属性,因此点语法无法直接访问title等字段。- 反射报错是因为
GetValue方法传入的参数错误:应该传入testAccess(即volumeInfo实例),而不是this(当前控制器实例)。
2. 正确的解决方法:创建对应嵌套类
为volumeInfo及其内部的嵌套结构创建专门的POCO类,替换GBAModel2中的Object类型,这样JSON反序列化工具会自动将数据映射到对应类的属性上,之后就能直接用点语法访问属性。
完整POCO定义:
public class GBAModel1 { public List<GBAModel2> items { get; set; } } public class GBAModel2 { public string id { get; set; } public string selfLink { get; set; } // 替换Object为自定义的VolumeInfo类 public VolumeInfo volumeInfo { get; set; } } 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; } } public class IndustryIdentifier { public string type { get; set; } public string identifier { get; set; } } public class ImageLinks { public string smallThumbnail { get; set; } public string thumbnail { get; set; } }
修改后访问属性的控制器代码:
[HttpGet("/booksApi")] public async Task<IActionResult> GetBooks() { var testResult = await _bookAction.GetBooksFromApi(); if(testResult?.Count > 0) { var bookTitle = testResult[0].volumeInfo.title; var bookAuthor = testResult[0].volumeInfo.authors?[0]; var thumbnailUrl = testResult[0].volumeInfo.imageLinks?.thumbnail; return Ok(new { Title = bookTitle, Author = bookAuthor, Thumbnail = thumbnailUrl }); } return NotFound("未找到对应图书"); }
3. 若临时使用反射的修正方法
如果不想创建完整的POCO类,也可以修正反射代码的错误:
var testAccess = testResult[0].volumeInfo; // GetValue第一个参数要传入目标对象testAccess,而不是this var try2 = testAccess.GetType().GetProperty("title")?.GetValue(testAccess, null); return Ok(try2);
不过这种方法类型不安全,建议优先使用创建POCO类的方式。
内容的提问来源于stack exchange,提问作者Juan21Guns
相关产品推荐
相关产品推荐

