在 Flutter News Toolkit 中获取类别

问题描述 投票:0回答:1

在尝试从类别端点检索类别时,我遇到了此错误:

_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()
是否不应该是列表而是地图?但它是像模板中那样定义的,所以也许这是错误之一?或者也许我没有看到什么?我完全迷失了,文档对我没有任何帮助,所以任何帮助将不胜感激。

flutter dart
1个回答
0
投票

问题似乎源于这样一个事实:您从

/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)

解决方法如下:

  1. 更新您的扩展方法以处理动态 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,
          );
        }
      }
    }
    
  2. 修改您的

    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),
        );
      }
    }
    
  3. 如有必要,调整您的

    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 响应以了解其结构。

© www.soinside.com 2019 - 2024. All rights reserved.