Type'List '不是'Map '

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

我正在开发一个依赖API REST调用的Flutter应用程序。API的响应有点复杂。调用我的API(例如api / products)时,我可以从日志中看到响应:但是我有这个错误:类型'List '不是类型'Map '的子类型我在互联网上看到了所有问题/答案,没有任何结果。我已经尝试过使用像https://my-json-server.typicode.com/typicode/demo/posts这样的简单RestAPI来工作。但这不是我的情况API响应示例:

[
   {
      "id":1,
      "tenant":{
         "id":1,
         "code":"company",
         "name":"company"
      },
      "type":{
         "code":"activity",
         "name":"Activité"
      },
      "subType":{
         "code":"ticket",
         "name":"Ticket"
      },
      "inventoryType":{
         "code":"external_source",
         "name":"Source externe"
      },
      "externalReference":"CAL6970",
      "externalSystem":{
         "code":"koedia",
         "name":"Koedia"
      },
      "active":true,
      "durationDays":12,
      "durationNights":14,
      "durationHours":9,
      "durationMinutes":10,
      "supplier":{
         "id":1,
         "tenant":{
            "id":1,
            "code":"company",
            "name":"company"
         },
         "name":"Jancarthier"
      },
      "group":null,
      "subGroup":null,
      "name":"Hôtel Koulnoué Village",
      "translations":[
         {
            "id":1,
            "name":"Hôtel Koulnoué Village",
            "locale":"fr"
         },
         {
            "id":24,
            "name":"Hôtel Koulnoué Village",
            "locale":"en"
         }
      ],
      "vatPercentage":"0.00",
      "longitude":null,
      "latitude":null,
      "departureTime":null,
      "arrivalTime":null,
      "arrivalDayPlus":1,
      "stars":4,
      "localities":[
         {
            "id":41,
            "locality":{
               "id":34,
               "code":"ARM",
               "name":"Armenia"
            },
            "role":{
               "code":"stop",
               "name":"Escale"
            }
         },
         {
            "id":49,
            "locality":{
               "id":55,
               "code":"hossegor",
               "name":"Hossegor"
            },
            "role":{
               "code":"drop_off",
               "name":"Retour"
            }
         },
         {
            "id":50,
            "locality":{
               "id":55,
               "code":"hossegor",
               "name":"Hossegor"
            },
            "role":{
               "code":"localisation",
               "name":"Localisation"
            }
         }
      ]
   }
]

StackTrace:

I/flutter ( 2865): type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'

[[Updated]:返回ProductsResponse.fromJson(response)而不是响应产品资料库:

import 'dart:async';
import 'package:day_experience/models/product/ProductResponse.dart';
import 'package:day_experience/networking/ApiProvider.dart';

class ProductRepository {
  ApiProvider _provider =  ApiProvider();

  Future<ProductsResponse> fetchProducts() async {

      final response = await _provider.getFromApi("products");
      // here line 11 where exception is thrown
      return ProductsResponse.fromJson(response);
  }


}

ProductsBloc:

import 'dart:async';

import 'package:day_experience/models/product/ProductResponse.dart';

import 'package:day_experience/networking/Response.dart';
import 'package:day_experience/repository/ProductRepository.dart';


class ProductBloc {
  ProductRepository _productRepository;
  StreamController _productListController;
  bool _isStreaming;
  StreamSink<Response<ProductsResponse>> get productListSink =>
      _productListController.sink;


  Stream<Response<ProductsResponse>> get productListStream =>
      _productListController.stream;

  ProductBloc() {
    _productListController = StreamController<Response<ProductsResponse>>();
    _productRepository = ProductRepository();
    _isStreaming = true;
    fetchProducts();

  }

  fetchProducts() async {
    productListSink.add(Response.loading('Getting Products.'));
    try {
     ProductsResponse productsResponse =
      await _productRepository.fetchProducts();
     if (_isStreaming) productListSink.add(Response.completed(productsResponse));
    } catch (e) {
      if (_isStreaming) productListSink.add(Response.error(e.toString()));
      print(e);
    }
  }

  dispose() {
    _isStreaming = false;
    _productListController?.close();
  }
}

响应:

class Response<T> {
  Status status;
  T data;
  String message;

  Response.loading(this.message) : status = Status.LOADING;
  Response.completed(this.data) : status = Status.COMPLETED;
  Response.error(this.message) : status = Status.ERROR;

  @override
  String toString() {
    return "Status : $status \n Message : $message \n Data : $data";
  }
}

enum Status { LOADING, COMPLETED, ERROR }

ApiProvider:

import 'package:day_experience/networking/CustomException.dart';
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';
import 'dart:async';

class ApiProvider {

  final String _baseApiUrl = "URL_API/";

  Future<dynamic> getFromApi(String url) async {
    var responseJson;
    try {
      final response = await http.get(Uri.encodeFull(_baseApiUrl + url),headers:{"Accept":"application/json"} );
      print(response);
      responseJson = _response(response);
    } on SocketException {
      throw FetchDataException('No Internet connection');
    }
    return responseJson;
  }

  dynamic _response(http.Response response) {
    switch (response.statusCode) {
      case 200:
        var responseJson = json.decode(response.body);

        print(responseJson);
        return responseJson;
      case 400:
        throw BadRequestException(response.body.toString());
      case 401:

      case 403:
        throw UnauthorisedException(response.body.toString());
      case 500:

      default:
        throw FetchDataException(
            'Error occured while Communication with Server with StatusCode : ${response.statusCode}');
    }
  }
}

型号:产品响应

import 'ExternalSystem.dart';
import './InventoryType.dart';
import './Localities.dart';
import 'SubType.dart';
import 'Supplier.dart';
import 'Tenant.dart';
import './Translation.dart';
import './Type.dart';

class ProductsResponse {
    final int id;
    final Tenant tenant;
    final Type type;
    final SubType subType;
    final InventoryType inventoryType;
    final String externalReference;
    final ExternalSystem externalSystem;
    final bool active;
    final int durationDays;
    final int durationNights;
    final int durationHours;
    final int durationMinutes;
    final Supplier supplier;
    final String name;
    final List<Translation> translations;
    final String vatPercentage;
    final int arrivalDayPlus;
    final int stars;
    final List<Localities> localities;
    final String group;
    final String subGroup;
    final double longitude;
    final double latitude;
    final String departureTime;
    final String arrivalTime;

    ProductsResponse({this.id, this.tenant , this.type, this.subType, this.inventoryType, this.externalReference, this.externalSystem, this.active, this.durationDays, this.durationNights, this.durationHours, this.durationMinutes, this.supplier, this.name, this.translations, this.vatPercentage, this.arrivalDayPlus, this.stars, this.localities, this.group, this.subGroup, this.longitude, this.latitude, this.departureTime, this.arrivalTime});

    factory ProductsResponse.fromJson(Map<String, dynamic> json) {
        return ProductsResponse(
            id: json['id'],
            tenant: json['tenant'] != null ? Tenant.fromJson(json['tenant']) : null,
            type: json['type'] != null ? Type.fromJson(json['type']) : null,
            subType: json['subType'] != null ? SubType.fromJson(json['subType']) : null,
            inventoryType: json['inventoryType'] != null ? InventoryType.fromJson(json['inventoryType']) : null,
            externalReference: json['externalReference'],
            externalSystem: json['externalSystem'] != null ? ExternalSystem.fromJson(json['externalSystem']) : null,
            active: json['active']?json['active']:null,
            durationDays: json['durationDays']?json['durationDays']:null,
            durationNights: json['durationNights']?json['durationNights']:null,
            durationHours: json['durationHours']?json['durationHours']:null,
            durationMinutes: json['durationMinutes']?json['durationMinutes']:null,
            supplier: json['supplier'] != null ? Supplier.fromJson(json['supplier']) : null,
            name: json['name']?json['name']:null,
            translations: json['translations'] != null ? (json['translations'] as List).map((i) => Translation.fromJson(i)).toList() : null,
            vatPercentage: json['vatPercentage']?json['vatPercentage']:null,
            arrivalDayPlus: json['arrivalDayPlus']?json['arrivalDayPlus']:null,
            stars: json['stars']?json['stars']:null,
            localities: json['localities'] != null ? (json['localities'] as List).map((i) => Localities.fromJson(i)).toList() : null,
            group: json['group'] != null ? json['group'] : null,
            subGroup: json['subGroup'] != null ? json['subGroup'] : null,
            longitude: json['longitude'] != null ? json['longitude'] : null,
            latitude: json['latitude'] != null ? json['latitude'] : null,
            departureTime: json['departureTime'] != null ? json['departureTime'] : null,
            arrivalTime: json['arrivalTime'] != null ? json['arrivalTime'] : null,
        );
    }

    Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['id'] = this.id;
        data['externalReference'] = this.externalReference;
        data['active'] = this.active;
        data['durationDays'] = this.durationDays;
        data['durationNights'] = this.durationNights;
        data['durationHours'] = this.durationHours;
        data['durationMinutes'] = this.durationMinutes;
        data['name'] = this.name;
        data['vatPercentage'] = this.vatPercentage;
        data['arrivalDayPlus'] = this.arrivalDayPlus;
        data['stars'] = this.stars;
        if (this.tenant != null) {
            data['tenant'] = this.tenant.toJson();
        }
       if (this.type != null) {
            data['type'] = this.type.toJson();
        }
        if (this.subType != null) {
            data['subType'] = this.subType.toJson();
        }
        if (this.inventoryType != null) {
            data['inventoryType'] = this.inventoryType.toJson();
        }
        if (this.externalSystem != null) {
            data['externalSystem'] = this.externalSystem.toJson();
        }
        if (this.supplier != null) {
            data['supplier'] = this.supplier.toJson();
        }
        if (this.translations != null) {
            data['translations'] = this.translations.map((v) => v.toJson()).toList();
        }
        if (this.localities != null) {
            data['localities'] = this.localities.map((v) => v.toJson()).toList();
        }
        if (this.group != null) {
            data['group'] = this.group;
        }
        if (this.subGroup != null) {
            data['subGroup'] = this.subGroup;
        }
        if (this.longitude != null) {
            data['longitude'] = this.longitude;
        }
        if (this.latitude != null) {
            data['latitude'] = this.latitude;
        }
        if (this.departureTime != null) {
            data['departureTime'] = this.departureTime;
        }
        if (this.arrivalTime != null) {
            data['arrivalTime'] = this.arrivalTime;
        }
      print(data);

        return data;
    }
}

[[Updated]:删除了ProductsRepository(只能通过bloc访问)产品视图

import 'package:day_experience/blocs/ProductBloc.dart';
import 'package:day_experience/models/product/ProductResponse.dart';
import 'package:day_experience/networking/Response.dart';
import 'package:day_experience/repository/ProductRepository.dart';
import 'package:day_experience/view/widget/Loading.dart';
import 'package:day_experience/view/widget/ProductList.dart';
import 'package:flutter/material.dart';
import 'package:day_experience/view/widget/Error.dart';


class ProductView extends StatefulWidget {
 @override
 _ProductViewState createState() => _ProductViewState();
}

class _ProductViewState extends State<ProductView> {
 ProductBloc _bloc = new ProductBloc();

 @override
 void initState() {
   super.initState();
   _bloc.fetchProducts();
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       elevation: 0.0,
       automaticallyImplyLeading: false,
       title: Text('Products',
           style: TextStyle(color: Colors.white, fontSize: 20)),
       backgroundColor: Color(0xFF333333),
     ),
     backgroundColor: Color(0xFF333333),
     body: RefreshIndicator(
       onRefresh: () => _bloc.fetchProducts(),
       child: StreamBuilder<Response<ProductsResponse>>(
         stream: _bloc.productListStream,
         builder: (context, snapshot) {
           if (snapshot.hasData) {
             switch (snapshot.data.status) {
               case Status.LOADING:
                 return Loading(loadingMessage: snapshot.data.message);
                 break;
               case Status.COMPLETED:
                 return ProductList(productList:snapshot.data.data);
                 break;
               case Status.ERROR:
                 return Error(
                   errorMessage: snapshot.data.message,
                   onRetryPressed: () => _bloc.fetchProducts(),
                 );
                 break;
             }
           }
           return Container();
         },
       ),
     ),
   );
 }


}


Tenant示例对其他对象的相同逻辑(Translation,Type,SubType ..]

class Tenant {
   final int id;
   final String code;
   final String name;

   Tenant({this.id, this.code, this.name});

   factory Tenant.fromJson(Map<String, dynamic> json) {
       return Tenant(
           id: json['id'], 
           code: json['code'], 
           name: json['name'], 
       );
   }

   Map<String, dynamic> toJson() {
       final Map<String, dynamic> data = new Map<String, dynamic>();
       data['id'] = this.id;
       data['code'] = this.code;
       data['name'] = this.name;
       return data;
   }
}
flutter dart
1个回答
0
投票

您可以在下面复制粘贴运行完整代码因为您的json字符串产生的是List<ProductResponse>而不是ProductResponse在您的代码中,您可以直接将response.body返回为String并使用productsResponseFromJson进行解析

代码段

List<ProductsResponse> productsResponseFromJson(String str) =>
List<ProductsResponse>.from(
    json.decode(str).map((x) => ProductsResponse.fromJson(x)));

Future<List<ProductsResponse>> fetchProducts() async {
  ApiProvider _provider = ApiProvider();
  String response = await _provider.getFromApi("products");
  // here line 11 where exception is thrown
  return productsResponseFromJson(response);
  //return ProductsResponse.fromJson(response);
}

Future<String> getFromApi(String url) async {

String _response(http.Response response) {
    switch (response.statusCode) {
      case 200:
        print(response.body);
        //var responseJson = jsonDecode(response.body);

        //print(responseJson);
        return response.body;

工作演示

enter image description here

完整代码

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// To parse this JSON data, do
//
//     final productsResponse = productsResponseFromJson(jsonString);

import 'dart:convert';

List<ProductsResponse> productsResponseFromJson(String str) =>
    List<ProductsResponse>.from(
        json.decode(str).map((x) => ProductsResponse.fromJson(x)));

String productsResponseToJson(List<ProductsResponse> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class ProductsResponse {
  int id;
  Tenant tenant;
  ExternalSystem type;
  ExternalSystem subType;
  ExternalSystem inventoryType;
  String externalReference;
  ExternalSystem externalSystem;
  bool active;
  int durationDays;
  int durationNights;
  int durationHours;
  int durationMinutes;
  Supplier supplier;
  dynamic group;
  dynamic subGroup;
  String name;
  List<Translation> translations;
  String vatPercentage;
  dynamic longitude;
  dynamic latitude;
  dynamic departureTime;
  dynamic arrivalTime;
  int arrivalDayPlus;
  int stars;
  List<Locality> localities;

  ProductsResponse({
    this.id,
    this.tenant,
    this.type,
    this.subType,
    this.inventoryType,
    this.externalReference,
    this.externalSystem,
    this.active,
    this.durationDays,
    this.durationNights,
    this.durationHours,
    this.durationMinutes,
    this.supplier,
    this.group,
    this.subGroup,
    this.name,
    this.translations,
    this.vatPercentage,
    this.longitude,
    this.latitude,
    this.departureTime,
    this.arrivalTime,
    this.arrivalDayPlus,
    this.stars,
    this.localities,
  });

  factory ProductsResponse.fromJson(Map<String, dynamic> json) =>
      ProductsResponse(
        id: json["id"],
        tenant: Tenant.fromJson(json["tenant"]),
        type: ExternalSystem.fromJson(json["type"]),
        subType: ExternalSystem.fromJson(json["subType"]),
        inventoryType: ExternalSystem.fromJson(json["inventoryType"]),
        externalReference: json["externalReference"],
        externalSystem: ExternalSystem.fromJson(json["externalSystem"]),
        active: json["active"],
        durationDays: json["durationDays"],
        durationNights: json["durationNights"],
        durationHours: json["durationHours"],
        durationMinutes: json["durationMinutes"],
        supplier: Supplier.fromJson(json["supplier"]),
        group: json["group"],
        subGroup: json["subGroup"],
        name: json["name"],
        translations: List<Translation>.from(
            json["translations"].map((x) => Translation.fromJson(x))),
        vatPercentage: json["vatPercentage"],
        longitude: json["longitude"],
        latitude: json["latitude"],
        departureTime: json["departureTime"],
        arrivalTime: json["arrivalTime"],
        arrivalDayPlus: json["arrivalDayPlus"],
        stars: json["stars"],
        localities: List<Locality>.from(
            json["localities"].map((x) => Locality.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "tenant": tenant.toJson(),
        "type": type.toJson(),
        "subType": subType.toJson(),
        "inventoryType": inventoryType.toJson(),
        "externalReference": externalReference,
        "externalSystem": externalSystem.toJson(),
        "active": active,
        "durationDays": durationDays,
        "durationNights": durationNights,
        "durationHours": durationHours,
        "durationMinutes": durationMinutes,
        "supplier": supplier.toJson(),
        "group": group,
        "subGroup": subGroup,
        "name": name,
        "translations": List<dynamic>.from(translations.map((x) => x.toJson())),
        "vatPercentage": vatPercentage,
        "longitude": longitude,
        "latitude": latitude,
        "departureTime": departureTime,
        "arrivalTime": arrivalTime,
        "arrivalDayPlus": arrivalDayPlus,
        "stars": stars,
        "localities": List<dynamic>.from(localities.map((x) => x.toJson())),
      };
}

class ExternalSystem {
  String code;
  String name;

  ExternalSystem({
    this.code,
    this.name,
  });

  factory ExternalSystem.fromJson(Map<String, dynamic> json) => ExternalSystem(
        code: json["code"],
        name: json["name"],
      );

  Map<String, dynamic> toJson() => {
        "code": code,
        "name": name,
      };
}

class Locality {
  int id;
  Tenant locality;
  ExternalSystem role;

  Locality({
    this.id,
    this.locality,
    this.role,
  });

  factory Locality.fromJson(Map<String, dynamic> json) => Locality(
        id: json["id"],
        locality: Tenant.fromJson(json["locality"]),
        role: ExternalSystem.fromJson(json["role"]),
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "locality": locality.toJson(),
        "role": role.toJson(),
      };
}

class Tenant {
  int id;
  String code;
  String name;

  Tenant({
    this.id,
    this.code,
    this.name,
  });

  factory Tenant.fromJson(Map<String, dynamic> json) => Tenant(
        id: json["id"],
        code: json["code"],
        name: json["name"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "code": code,
        "name": name,
      };
}

class Supplier {
  int id;
  Tenant tenant;
  String name;

  Supplier({
    this.id,
    this.tenant,
    this.name,
  });

  factory Supplier.fromJson(Map<String, dynamic> json) => Supplier(
        id: json["id"],
        tenant: Tenant.fromJson(json["tenant"]),
        name: json["name"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "tenant": tenant.toJson(),
        "name": name,
      };
}

class Translation {
  int id;
  String name;
  String locale;

  Translation({
    this.id,
    this.name,
    this.locale,
  });

  factory Translation.fromJson(Map<String, dynamic> json) => Translation(
        id: json["id"],
        name: json["name"],
        locale: json["locale"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "name": name,
        "locale": locale,
      };
}

void main() {
  runApp(MyApp());
}

Future<List<ProductsResponse>> fetchProducts() async {
  ApiProvider _provider = ApiProvider();
  String response = await _provider.getFromApi("products");
  // here line 11 where exception is thrown
  return productsResponseFromJson(response);
  //return ProductsResponse.fromJson(response);
}

class ApiProvider {
  Future<String> getFromApi(String url) async {
    var responseJson;
    try {
      //final response = await http.get(Uri.encodeFull(_baseApiUrl + url),headers:{"Accept":"application/json"} );
      String jsonString = '''
      [
   {
      "id":1,
      "tenant":{
         "id":1,
         "code":"company",
         "name":"company"
      },
      "type":{
         "code":"activity",
         "name":"Activité"
      },
      "subType":{
         "code":"ticket",
         "name":"Ticket"
      },
      "inventoryType":{
         "code":"external_source",
         "name":"Source externe"
      },
      "externalReference":"CAL6970",
      "externalSystem":{
         "code":"koedia",
         "name":"Koedia"
      },
      "active":true,
      "durationDays":12,
      "durationNights":14,
      "durationHours":9,
      "durationMinutes":10,
      "supplier":{
         "id":1,
         "tenant":{
            "id":1,
            "code":"company",
            "name":"company"
         },
         "name":"Jancarthier"
      },
      "group":null,
      "subGroup":null,
      "name":"Hôtel Koulnoué Village",
      "translations":[
         {
            "id":1,
            "name":"Hôtel Koulnoué Village",
            "locale":"fr"
         },
         {
            "id":24,
            "name":"Hôtel Koulnoué Village",
            "locale":"en"
         }
      ],
      "vatPercentage":"0.00",
      "longitude":null,
      "latitude":null,
      "departureTime":null,
      "arrivalTime":null,
      "arrivalDayPlus":1,
      "stars":4,
      "localities":[
         {
            "id":41,
            "locality":{
               "id":34,
               "code":"ARM",
               "name":"Armenia"
            },
            "role":{
               "code":"stop",
               "name":"Escale"
            }
         },
         {
            "id":49,
            "locality":{
               "id":55,
               "code":"hossegor",
               "name":"Hossegor"
            },
            "role":{
               "code":"drop_off",
               "name":"Retour"
            }
         },
         {
            "id":50,
            "locality":{
               "id":55,
               "code":"hossegor",
               "name":"Hossegor"
            },
            "role":{
               "code":"localisation",
               "name":"Localisation"
            }
         }
      ]
   }
]
    ''';

      http.Response response = http.Response(jsonString, 200);
      print(response);
      responseJson = _response(response);
    } on Exception {
      //throw FetchDataException('No Internet connection');
    }
    return responseJson;
  }

  String _response(http.Response response) {
    switch (response.statusCode) {
      case 200:
        print(response.body);
        //var responseJson = jsonDecode(response.body);

        //print(responseJson);
        return response.body;
      /* case 400:
        throw BadRequestException(response.body.toString());
      case 401:

      case 403:
        throw UnauthorisedException(response.body.toString());
      case 500:

      default:
        throw FetchDataException(
            'Error occured while Communication with Server with StatusCode : ${response.statusCode}');*/
    }
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  List<ProductsResponse> productResponseList;

  void _incrementCounter() async {
    productResponseList = await fetchProducts();
    print('${productResponseList[0].inventoryType}');
    setState(() {
      _counter++;
    });
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            productResponseList == null
                ? CircularProgressIndicator()
                : Text('${productResponseList[0].inventoryType.name}'),
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.