Reputation: 2527
Here is yet another "Expected a value of type 'Map<String, dynamic>', but got one of type 'List'" questions. I've scoured several Stack Overflow posts on the subject including the following:
I've also tried to follow this Flutter cookbook example.
So, I want to simply call an API that returns a JSON list of lists. Each one of those inner lists of strings corresponds to a class object I've defined below called 'Provider'. I have created class factory methods to map the JSON to my 'Provider' class:
factory Provider.fromJsonMap(Map<String, dynamic> json) # The preferred way to call
factory Provider.fromJsonListDynamic(List<dynamic> json)
I would prefer to use the first factory method, rather than the second, because I would like to reference each string by it's name, not an array index as you will see in the code.
UPDATE:
As @OmiShah pointed out, the serialized JSON I was passing back from my Python API had no schema, so my deserialized JSON response contains no information about the data. So I couldn't make references to the data like so:
json["name"]
So I changed my Python API to serialize a list of strongly typed class objects and pass that I back to flutter as JSON. Here is a sample of what that data looks like now in Python before it's passed back to flutter:
[
Provider(name='Google Translate API', id='google.translate_api.v3', img_url='ggl.png'),
Provider(name='Microsoft Translator', id='microsoft.translator.v3', img_url='mcs.png'),
Provider(name='Amazon Translate API', id='amazon.translate.api.v2', img_url='aws.png'),
]
And this is what that data looks like after it's deserialized by flutter:
final List<dynamic> listProviders = jsonDecode(response.body);
// contents of listProviders
[
"Provider(name='Google Translate API', id='google.translate_api.v3', img_url='ggl.png')",
"Provider(name='Microsoft Translator', id='microsoft.translator.v3', img_url='mcs.png')",
"Provider(name='Amazon Translate API', id='amazon.translate.api.v2', img_url='aws.png')",
]
I then cast it as list of maps (listMap) and then attempt to map each object to my 'Provider' class object (providerMap) and finally I convert providerMap to a list:
final List<dynamic> listProviders = jsonDecode(response.body);
final List<Map<String, dynamic>> listMap = listProviders.cast<Map<String, dynamic>>();
var providerMap = listMap.map<Provider>((json) => Provider.fromJsonMap(json));
List<Provider> providers = providerMap.toList();
I get the following error when I attempt to convert it to a list:
Error: Expected a value of type 'Map<String, dynamic>', but got one of type 'String'
I am relatively new to Flutter. I would like to pass data around as strongly typed objects and I want to do it in a way that makes sense.
The custom class object I created:
class Provider
{
final String name;
final String id;
final String imageUrl;
Provider( {required this.name, required this.id, required this.imageUrl });
// Calling this function fails
factory Provider.fromJsonMap(Map<String, dynamic> json)
{
Provider provider = Provider
(
name: json["name"] as String,
id: json["id"] as String,
imageUrl: json["imageUrl"] as String,
);
return provider;
}
// Calling this function works
factory Provider.fromJsonListDynamic(List<dynamic> json)
{
Provider provider = Provider
(
name: json[0] as String,
id: json[1] as String,
imageUrl: json[2] as String,
);
return provider;
}
}
And it's called in this Future async function in a PageState class:
class _DetectorPageState extends State<DetectorPage>
{
...
Future<List<Provider>> getMtProviders() async
{
Uri url = Uri.parse(SOME_API_URL);
http.Response response = await http.get(url);
if (response.statusCode == 200)
{
// THIS CODE DOES NOT WORK
final List<dynamic> listProviders = jsonDecode(response.body);
final List<Map<String, dynamic>> listMap = listProviders.cast<Map<String, dynamic>>();
var providerMap = listMap.map<Provider>((json) => Provider.fromJsonMap(json));
List<Provider> providers = providerMap.toList();
// THIS CODE WORKS
// List<dynamic> listProviders = jsonDecode(response.body);
// List<Provider> providers = listProviders.map((provider) => Provider.fromJsonListDynamic(provider as List<dynamic>)).toList();
return providers;
}
else
{
print('Request failed with status: ${response.statusCode}.');
return [];
}
}
}
Upvotes: 2
Views: 439
Reputation: 2527
The problem was with the JSON serialization in my python API. I need to serialize a list of dictionary objects that contain my data and not serialize a list of custom class objects.
providers_dict = \ # Sample Data
[
{"name": "Google Translate API", "id": "google.translate_api.v3", "img_url": "ggl_translate.png"},
{"name": "Microsoft Translator", "id": "microsoft.translator.v3", "img_url": "mcs_translate.png"},
{"name": "Amazon Translate API", "id": "amazon.translate.api.v2", "img_url": "aws_translate.png"},
]
Then you call the json.dumps method to serialize the data into JSON like so:
providers_json = json.dumps(providers_dict)
return providers_json
Upvotes: 1