Search code examples
jsonflutterdartfastapi

Why dart:convert :: json.encoder turns everything into a string?


I am trying to send an HTTP PUT request to FastAPI backend from flutter code. I jsonify the data before I put into the request body. Here are my data classes:

class ListeningHistory {
  final String userId;
  List<BareMinPost> podcasts;

  ListeningHistory({required this.userId, required this.podcasts});

  Map<String, dynamic> toJson() {
    podcasts = []; // for simplicity's sake
    return {'user_id': userId, 'podcasts': jsonEncode(podcasts)};
  }

Please note that I intentionally replaced the actual podcasts with an empty array above for the sake of simplicity for this example.

class BareMinPost {
  final String title;
  final String publishedDate;
  final int pausedAt;

  BareMinPost({
    required this.title,
    required this.publishedDate,
    required this.pausedAt,
  });

  BareMinPost.fromJson(Map<String, dynamic> json)
      : title = json['title'] as String,
        publishedDate = json['publishedDate'] as String,
        pausedAt = json['pausedAt'] as int;

  Map<String, dynamic> toJson() =>
      {'title': title, 'publishedDate': publishedDate, 'pausedAt': pausedAt};

  @override
  String toString() {
    return "BareMinPost - title: $title   publishedDate: $publishedDate   puasedAt: $pausedAt";
  }
}

I use the following piece of code to json encode the data:

    import 'dart:convert';
...
    ListeningHistory hist = ListeningHistory(
        userId: "$userId", podcasts: previouslyListenedTo);
    String reqBody = json.encode(hist);
    dualLog(logger, '------------------->>>>-----$reqBody');

log line prints out as follows:

------------------->>>>-----{"user_id":"org.couchdb.users:alp","podcasts":"[]"}

Please note the double quotes around the array at the end. These, I believe should not be there but they are for some reason.

And eventually when I send this request the fastapi server, it complains as follows:

ERROR: {"detail":[{"type":"list_type","loc":["body","podcasts"],"msg":"Input should be a valid list","input":"[]"}]}

What am I missing here ?


Solution

  • I expanded the comment and included an example:

    import 'dart:convert' show JsonUnsupportedObjectError, jsonEncode, jsonDecode;
    
    class BareMinPost {
      BareMinPost({required this.title});
      final String title;
    
      Map<String, dynamic> toJson() => {'title': title};
    
      factory BareMinPost.fromJson(Map<String, dynamic> json) {
        // Validate input
        if (json
            case {
              'title': String title,
            }) {
          return BareMinPost(title: title);
        } else {
          throw JsonUnsupportedObjectError(json,
              cause: 'BareMinPost: Json validation failed');
        }
      }
    }
    
    class ListeningHistory {
      final String userId;
      List<BareMinPost> podcasts;
    
      ListeningHistory({required this.userId, required this.podcasts});
    
      Map<String, dynamic> toJson() {
        return {
          'user_id': userId,
          'podcasts': podcasts
              .map(
                (e) => e.toJson(),
              )
              .toList()
        };
      }
    
      factory ListeningHistory.fromJson(Map<String, dynamic> json) {
        // Validate input
        if (json
            case {
              'user_id': String userId,
              'podcasts': List podcasts,
            }) {
          return ListeningHistory(
            userId: userId,
            podcasts: podcasts.map((e) => BareMinPost.fromJson(e)).toList(),
          );
        } else {
          throw JsonUnsupportedObjectError(json,
              cause: 'ListeningHistory: Json validation failed');
        }
      }
    }
    

    Usage:

    void main(List<String> args) {
      final history = ListeningHistory(userId: 'json1997', podcasts: [
        BareMinPost(title: 'Song A'),
        BareMinPost(title: 'Song B'),
      ]);
    
      // Encoding
      print('Encoding: ...');
      print(
        history.toJson(),
      ); // Prints: {user_id: json1997, podcasts: [{title: Song A}, {title: Song B}]}
      final jsonString = jsonEncode(history.toJson());
      print(
        jsonString,
      ); // Prints: {"user_id":"json1997","podcasts":[{"title":"Song A"},{"title":"Song B"}]}
    
      // Decoding
      print('\nDecoding ...');
      final jsonMap = jsonDecode(jsonString);
      print(jsonMap);
      final historyClone = ListeningHistory.fromJson(jsonMap);
    }
    

    The console output is listed below:

    Encoding: ...
    {user_id: json1997, podcasts: [{title: Song A}, {title: Song B}]}
    {"user_id":"json1997","podcasts":[{"title":"Song A"},{"title":"Song B"}]}
    
    Decoding ...
    {user_id: json1997, podcasts: [{title: Song A}, {title: Song B}]}