6

I'm successfully printing my response as String from my YouTube JSON url, but when I try to serialize through the "items" I get the following error Unhandled exception: type 'List' is not a subtype of type 'Map' of 'json' where List is from dart:core Map is from dart:core

Here is my code...

class CardInfo {
  //Constructor
  String id;
  String description;
  String role;
  //int score;

  CardInfo.fromJson(Map json) {
    this.id = json['vieoId'];
    this.description = json['description'];
    this.role = json['title'];
    //this.score = json['score'];
  }
}

Future getData() async {
    String url = 'YouTube url';
    var httpClient  = createHttpClient();
    var response = await httpClient.get(url);
    Map data = JSON.decode(response.body);
    //String ip = data['items'];
    var ci = new CardInfo.fromJson(data['items']);

    //print(data['items']);
    print(ci.id);
    //print(ci.description);
    //print(ci.role);

    if (!mounted) return;


    setState(() {});

  }

print(data['items'] is printing, but print(ci.id) or any Card Info variables throws the above error.

**** Log of print(data);

{kind: youtube#searchListResponse, etag: "VPWTmrH7dFmi4s1RqrK4tLejnRI/P9wyOxsXEuXOCvj7znCun2-EykU", nextPageToken: CAMQAA, regionCode: US, pageInfo: {totalResults: 1000000, resultsPerPage: 3}, items: [{kind: youtube#searchResult, etag: "VPWTmrH7dFmi4s1RqrK4tLejnRI/Csl1kQhnOsbs0j4_336zJAN176k", id: {kind: youtube#video, videoId: e3pUxU_bE6w}, snippet: {publishedAt: 2017-09-14T09:43:17.000Z, channelId: UCbD8EppRX3ZwJSou-TVo90A, title: [PRISTIN - We Like] KPOP TV Show | M COUNTDOWN 170914 EP.541, description: KPOP Chart Show M COUNTDOWN | EP.541 - PRISTIN - We Like ▷Watch more video clips: http://MCOUNTDOWN-KPOP2017 [Kor Ver.] 프리티 ..., thumbnails: {default: {url: https://i.ytimg.com/vi/e3pUxU_bE6w/default.jpg, width: 120, height: 90}, medium: {url: https://i.ytimg.com/vi/e3pUxU_bE6w/mqdefault.jpg, width: 320, height: 180}, high: {url: https://i.ytimg.com/vi/e3pUxU_bE6w/hqdefault.jpg, width: 480, height: 360}}, channelTitle: Mnet K-POP, liveBroadcastContent: none}}, {kind: youtube#searchResult, etag: "VPWTmrH7dFmi4s1RqrK4tLejnRI/1JCCNBPNbFeusCp_9-pl4i8q5OU", id: {kind: youtube#video, videoId: Cc4hO9RLdl4}, snippet: {publishedAt: 2017-09-14T10:37:29.000Z, channelId: UCbD8EppRX3ZwJSou-TVo90A, title: [EXO - Power] KPOP TV Show | M COUNTDOWN 170914 EP.541, description: KPOP Chart Show M COUNTDOWN | EP.541 - EXO - Power ▷Watch more video clips: http://MCOUNTDOWN-KPOP2017 [Kor Ver.] Power Up! '#EXO' 여기 ..., thumbnails: {default: {url: https://i.ytimg.com/vi/Cc4hO9RLdl4/default.jpg, width: 120, height: 90}, medium: {url: https://i.ytimg.com/vi/Cc4hO9RLdl4/mqdefault.jpg, width: 320, height: 180}, high: {url: https://i.ytimg.com/vi/Cc4hO9RLdl4/hqdefault.jpg, width: 480, height: 360}}, channelTitle: Mnet K-POP, liveBroadcastContent: none}}, {kind: youtube#searchResult, etag: "VPWTmrH7dFmi4s1RqrK4tLejnRI/ZnYC4e5evyfldkM67HsDuV8Yh3E", id: {kind: youtube#video, videoId: BBcOM25wrVo}, snippet: {publishedAt: 2017-08-18T15:21:48.000Z, channelId: UCtFtO4By4czgkYGvEXvJu0A, title: Kpop Banned Dance: MV vs LIVE, description: Kpop Banned Dance: MV vs LIVE Koreas biggest broadcasting companies has strict rules and standards on what lyrics and dances moves can be performed., thumbnails: {default: {url: https://i.ytimg.com/vi/BBcOM25wrVo/default.jpg, width: 120, height: 90}, medium: {url: https://i.ytimg.com/vi/BBcOM25wrVo/mqdefault.jpg, width: 320, height: 180}, high: {url: https://i.ytimg.com/vi/BBcOM25wrVo/hqdefault.jpg, width: 480, height: 360}}, channelTitle: Kpop Corn, liveBroadcastContent: none}}]}

*** UPDATE WITH FOR LOOP STATEMENT

Here is code for my for loop that's returning a type 'String' is not a subtype of type 'int' of 'index' error...

Map data = JSON.decode(response);
var videos = data['items'];
for (var items in videos['snippet']){
      print(items);
    }

Running a loop through items in videos gives me 3 separate entries for the 3 videos I'm looking for - including snippets. Trying to get the individual snippets is failing. Please point me in the right direction.

4
  • I believe you are passing the value of the key "items", which from the look of it is actually a List not a Map, your constructor should receive a Map but instead you are passing a List. Hence, you get the error. Otherwise, can you show me print(data) to see how your database is constructed ? Commented Sep 14, 2017 at 23:39
  • @aziza Thank you for the lesson below. Map = NSDictionary and List = NSArray. I've noticed that there's an itar and iter to iterate through a List.; I'm reading documentation on those now. I've added print(data) to my question. Please help get Maps of titles, videoId's, descriptions, and "medium" thumbnail url's. Thank you. Commented Sep 15, 2017 at 3:46
  • 1
    From what I see you are trying to access the values of snippet of items. So, you need to loop over your data['items'] index by index, and then check when you reach the "snippet" key, and pass this particular map to your class constructor. Commented Sep 15, 2017 at 18:01
  • @aziza Attempting to look through items to get to my snippets threw the above error. Please review. Commented Sep 16, 2017 at 20:16

3 Answers 3

20

It looks like data['items'] is a List (i.e. a JSON Array), not a Map.

You can use list comprehension methods to help here:

final items = (data['items'] as List).map((i) => new CardInfo.fromJson(i));
for (final item in items) {
  print(item.id);
}
Sign up to request clarification or add additional context in comments.

5 Comments

'data[items]' is also a List, or what SWIFT would call a Dictionary. Can I use a for statement to iterate through 'item's the same way?
@CharlesJr that is not true, a Dictionary is not the same thing as a List. They are two different collections. In fact, a Dictionary is actually a Map. Please read my comment on the question and try to check print (data['items']), you will find that your value for items is actually a List of a Map, something similar to this 'items': [{..:..},{..:..}....]
@matanlurey I'm having issues with my for loop. Please take a look at my updated Question and add it to your answer.
@matanlurey There is another Array inside my items. 'items': [{'SubItem':[ ]},{'SubItem':[ ]}....] How should i handle this situation
I used .toList() at the end to populate properly a list widget.
1

The following line gives you the List of items.

var videos = data['items'];

and you get the error because of this line

for(var items in videos['snippet'])

In the previous line you think you are iterating on the data inside snippet, while in fact, you are trying to iterate on the index 'snippet' inside the list of videos, which does not make sense because iterating over any list happens using integer values videos[0] , videos [1], videos [2] .. while you are passing a String 'snippet'

You need first to iterate on your videos list item by item (each item is a Map). Store each Map in a variable. then you can access the values of snippet by myMap['snippet']

    Map data = JSON.decode(response);
    var videos = data['items']; //returns a List of Maps
    for (var items in videos){ //iterate over the list
    Map myMap = items; //store each map 
    print(myMap['snippet']);
        }

See if this solves your problem.

4 Comments

Great! Now I have 3 individual snippets which I'm assuming I can run similar maps to and index on my Card widgets. I believe you have a similar SO question that I can draw from.
Yes, you can now pass the snippet to your class constructor, you might want to store their data outside the loop first. Glad it is working for you.
@CharlesJr I have added more details about why did you get this error.
1

I would Love to share this and some expert can also please improve this codes, After alot of hours have battle with it.

Model Class

class Testimony{
    String fullname;
   String testimony;

   Testimony({this.fullname,
     this.testimony}); 

    factory Testimony.fromJson(Map<String, dynamic> json) => new Testimony(
      fullname: json['fullname'] as String,
       testimony: json['testimony'] as String,  
         );

         }

API CLASS

List<Testimony> ToListandMap (String responseBody) {
 Map data = json.decode(responseBody);
    var videos = data['testimonies']; //returns a List of Maps
  final casting =  videos.cast<Map<String, dynamic>>();
   return casting.map<Testimony>((json) => Testimony.fromJson(json)).toList();
    }

Future<List<Testimony>> fetchTestimonies(http.Client client) async {
       final response = await client.get('https://tryjambcbt.com/api/testimonies');

         return ToList(response.body);
         }

MainWidget for UI

FutureBuilder<List<Testimony>>(
    future: fetchTestimonies(http.Client()),
    builder: (context, snapshot) {
      if (snapshot.hasError) print(snapshot.error);
      return snapshot.hasData
          ? TestimonyList(testimony: snapshot.data)
          : Center(child: CircularProgressIndicator());
    },
  ),

Widget

class TestimonyList extends StatelessWidget {
final List<Testimony> testimony;

TestimonyList({Key key, this.testimony}) : super(key: key);

  @override
 Widget build(BuildContext context) {
  return ListView.builder(
    physics: BouncingScrollPhysics(),
    padding: EdgeInsets.only(bottom: 10),
    shrinkWrap: true,
    scrollDirection: Axis.vertical,
    itemCount: testimony.length,
    itemBuilder: (context, index) {
    return Padding(
      padding: EdgeInsets.only(right: 10),
      child: Text(testimony[index].testimony)
       );
      },
    );
   }
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.