在尝试从类别端点检索类别时,我遇到了此错误:
_TypeError (type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast)
此错误是由 myprojectname_api_client.dart 扩展中的返回引发的:
Future<CategoriesResponse> getCategories() async {
final uri = Uri.parse('$_baseUrl/categories');
final response = await _httpClient.get(
uri,
headers: await _getRequestHeaders(),
);
final body = response.json();
if (response.statusCode != HttpStatus.ok) {
throw BernardinaiApiRequestFailure(
body: body,
statusCode: response.statusCode,
);
}
return CategoriesResponse.fromJson(body);
}
//------------
extension on http.Response {
Map<String, dynamic> json() {
try {
final decodedBody = utf8.decode(bodyBytes);
log("MyLog $body");
return jsonDecode(decodedBody) as Map<String, dynamic>;
} catch (error, stackTrace) {
Error.throwWithStackTrace(
BernardinaiApiMalformedResponse(error: error),
stackTrace,
);
}
}
}
日志显示
decodeBody
包含来自类别端点的完整 JSON。
我的自定义类中实现
getCategories
类的 NewsDataSource
方法如下所示:
Future<List<Category>> getCategories() async {
final uri = Uri.parse('https://www.mynewsurl.com/wp-json/wp/v2/categories');
final categoriesResponse = await http.get(uri);
if (categoriesResponse.statusCode == HttpStatus.ok) {
// Parse the response body into a Map
final Map<String, dynamic> categoriesData = jsonDecode(categoriesResponse.body) as Map<String, dynamic>;
// Access the 'categories' list from the map
final List<dynamic> categoriesList = categoriesData['categories'] as List<dynamic>;
log("MyLog categoriesList: $categoriesList");
// Extract category names and return them as a list of Category enums
final List<Category> categories = categoriesList.map((dynamic categoryData) {
final String categoryName = categoryData as String; // Explicitly cast to String
log("MyLog categoryName: $categoryName");
return Category.fromString(categoryName); // Use fromString to get the enum
}).toList();
log("MyLog categories to return: $categories");
return categories; // Returns list of category names
} else {
// Handle error cases by throwing a custom exception
throw BernardinaiApiRequestFailure(
statusCode: categoriesResponse.statusCode,
body: jsonDecode(categoriesResponse.body) as Map<String, dynamic>,
);
}
}
///------
class CategoriesInfo {
const CategoriesInfo(this.slug);
final String slug;
CategoriesInfo.fromJson(Map<String, dynamic> json)
: slug = _isKebabCase(json['slug'] as String)
? _convertToCamelCase(json['slug'] as String)
: json['slug'] as String;
Map<String, dynamic> toJson() {
log("mano as veikiu");
return {
'slug': slug,
};
}
static bool _isKebabCase(String input) {
log("MyLog checking what case is used");
return input.contains('-');
}
static String _convertToCamelCase(String input) {
log("MANO slugas: $input");
List<String> parts = input.split('-');
if (parts.length == 1) return input;
String camelCase = parts[0];
for (int i = 1; i < parts.length; i++) {
camelCase += parts[i][0].toUpperCase() + parts[i].substring(1);
}
return camelCase;
}
}
我尝试将扩展类型更改为 List,但是所有其他方法都抛出相同的错误,所以现在我想知道
Future<List<Category>> getCategories()
是否不应该是列表而是地图?但它是像模板中那样定义的,所以也许这是错误之一?或者也许我没有看到什么?我完全迷失了,文档对我没有任何帮助,所以任何帮助将不胜感激。
问题似乎源于这样一个事实:您从
/wp-json/wp/v2/categories
端点接收到的 JSON 响应实际上是一个 list (JSON 术语中的数组),而不是映射(对象)。这导致类型转换为 Map<String, dynamic>
失败,导致错误:
_TypeError (type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast)
解决方法如下:
更新您的扩展方法以处理动态 JSON 类型:
您可以让它返回动态类型并相应地处理映射和列表,而不是将解码后的 JSON 转换为
Map<String, dynamic>
。
extension on http.Response {
dynamic json() {
try {
final decodedBody = utf8.decode(bodyBytes);
log("MyLog $decodedBody");
return jsonDecode(decodedBody);
} catch (error, stackTrace) {
Error.throwWithStackTrace(
BernardinaiApiMalformedResponse(error: error),
stackTrace,
);
}
}
}
修改您的
getCategories
方法来处理列表:
更改解析逻辑以反映 JSON 是类别列表。
Future<List<Category>> getCategories() async {
final uri = Uri.parse('https://www.mynewsurl.com/wp-json/wp/v2/categories');
final categoriesResponse = await http.get(uri);
if (categoriesResponse.statusCode == HttpStatus.ok) {
// Parse the response body into a List
final List<dynamic> categoriesData = jsonDecode(categoriesResponse.body) as List<dynamic>;
log("MyLog categoriesData: $categoriesData");
// Map each item in the list to a Category object
final List<Category> categories = categoriesData.map((dynamic categoryData) {
final Map<String, dynamic> categoryMap = categoryData as Map<String, dynamic>;
final String categoryName = categoryMap['slug'] as String;
log("MyLog categoryName: $categoryName");
return Category.fromString(categoryName); // Use fromString to get the enum or create a Category instance
}).toList();
log("MyLog categories to return: $categories");
return categories; // Returns list of Category objects
} else {
// Handle error cases by throwing a custom exception
throw BernardinaiApiRequestFailure(
statusCode: categoriesResponse.statusCode,
body: jsonDecode(categoriesResponse.body),
);
}
}
如有必要,调整您的
CategoriesInfo
课程:
确保您的
CategoriesInfo.fromJson
构造函数正确地从 JSON 映射初始化对象。
class CategoriesInfo {
const CategoriesInfo(this.slug);
final String slug;
CategoriesInfo.fromJson(Map<String, dynamic> json)
: slug = _isKebabCase(json['slug'] as String)
? _convertToCamelCase(json['slug'] as String)
: json['slug'] as String;
// Rest of your code...
}
说明:
JSON 结构: 端点返回类别对象的 JSON 数组(列表),而不是 JSON 对象(映射)。列表中的每个类别对象都包含有关类别的详细信息。
类型转换: 尝试将 JSON 列表转换为
Map<String, dynamic>
会导致类型错误,因为列表不是映射。通过将其解析为 List<dynamic>
,您可以迭代每个项目。
处理每个类别:
categoriesData
列表中的每个项目都是代表一个类别的地图。在访问每个 dynamic
项目的字段之前,您需要将其转换为 Map<String, dynamic>
。
错误处理:确保您的错误处理不会假设响应正文是地图。抛出
BernardinaiApiRequestFailure
时,可以直接传递解码后的 JSON,无需强制转换。
其他提示:
日志记录:使用日志记录来检查 JSON 响应的实际结构。这可以帮助您了解如何正确解析它。
类型安全:虽然使用
dynamic
可能会有所帮助,但目标是尽可能具体地说明您的类型,以便在编译时捕获错误。
API 文档: 请始终参考 API 文档或检查原始 JSON 响应以了解其结构。