Flutter / Dart — Парсинг JSON на модель

Мне было интересно, может ли кто-нибудь помочь, пожалуйста? Я новичок в Flutter / Dart и пытаюсь разобрать вложенный ответ JSON в модель. Я использовал генератор JSON to Dart, который, похоже, работал хорошо, за исключением случаев, когда он анализирует ответы.

Я подозреваю, что проблема в том, что ответы различаются по результатам — иногда это может быть ноль, один массив или несколько.

Запуск .runtimeType показал мне, что он может возвращать null, если он пуст, List<dynamic>, если существует только один массив, и _InternalLinkedHashMap<String, dynamic>, если их несколько.

Я пробовал много разных подходов, чтобы попытаться решить эту проблему, и просмотрел много разных ответов StackOverflow, но, похоже, ничего не работает. Ошибка просто меняется с каждым моим изменением.

Ниже мой код и моя ошибка.

Ошибка: _TypeError (type '(dynamic) => Null' is not a subtype of type '(String, dynamic) => void' of 'f')

Код:

class VideoComments {
  int id;
  String comment;
  int uid;
  int likes;
  bool isLikedByUser;
  String posterProfilePic;
  String posterUsername;
  List<Responses> responses;

  VideoComments(
      {this.id,
      this.comment,
      this.uid,
      this.likes,
      this.isLikedByUser,
      this.posterProfilePic,
      this.posterUsername,
      this.responses});

  VideoComments.fromJson(Map<String, dynamic> json) {
    print("RESP: ${json['responses'].runtimeType}");

    id = json['id'];
    comment = json['comment'];
    uid = json['uid'];
    likes = json['likes'];
    isLikedByUser = json['isLikedByUser'];
    posterProfilePic = json['poster_profile_pic'];
    posterUsername = json['poster_username'];

    if (json['responses'] != null) {
      List<Responses> responses = [];
      json['responses'].forEach((v) {
        responses.add(new Responses.fromJson(v));
      });
    }
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['comment'] = this.comment;
    data['uid'] = this.uid;
    data['likes'] = this.likes;
    data['isLikedByUser'] = this.isLikedByUser;
    data['poster_profile_pic'] = this.posterProfilePic;
    data['poster_username'] = this.posterUsername;
    if (this.responses != null) {
      data['responses'] = this.responses.map((v) => v.toJson()).toList();
    }
    return data;
  }
}

class Responses {
  int id;
  String comment;
  int uid;
  int likes;
  bool isLikedByUser;

  Responses({this.id, this.comment, this.uid, this.likes, this.isLikedByUser});

  Responses.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    comment = json['comment'];
    uid = json['uid'];
    likes = json['likes'];
    isLikedByUser = json['isLikedByUser'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['comment'] = this.comment;
    data['uid'] = this.uid;
    data['likes'] = this.likes;
    data['isLikedByUser'] = this.isLikedByUser;
    return data;
  }
}

Любая помощь приветствуется!

См. также:  Ожидается, что JsonSerializer.Deserialize вызовет исключение при десериализации другого класса

Не уверен, пробовали ли вы это — у меня это сработало: flutter. dev / docs / development / data-and-backend / Проблема в том, что вам все равно нужно создать класс самостоятельно, но, по крайней мере, отображение json выполняется правильно (и с нулевой безопасностью).   —  person James P    schedule 23.06.2021

Спасибо, Андрия. Я видел это, я не уверен, что нахожусь на уровне владения Dart / Flutter, чтобы использовать это. Кажется, намного выше моего уровня мастерства   —  person James P    schedule 23.06.2021

когда вы используете онлайн-инструменты для преобразования JSON в Dart, например — dripcoding.com/json-to- dart, затем всегда проверяйте тип данных каждой переменной. Вы получаете сообщение об ошибке, потому что одна из переменных определена как STRING, но фактическое значение — NON String value. Вы можете сделать одно — изменить тип данных всех переменных на динамический. это решит проблему.   —  person James P    schedule 06.07.2021

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 4
  1. James P

    Просто для удовольствия — я попытался реализовать то же самое с помощью json_serializable, так как рекомендовал это в своем комментарии. Надеюсь, это покажет вам, что это не так уж и сложно — на самом деле, это проще, чем на самом деле выяснить, как кодировать это самостоятельно.

    Сначала я начал с пустого проекта Flutter.

    Во-вторых, я добавил необходимые зависимости в pubspec.yaml:

    dependencies:
      flutter:
        sdk: flutter
    cupertino_icons: ^1.0.2
    
    # Added this
    json_annotation: ^4.0.1
      
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
    
      # and these two...
      build_runner: ^2.0.4
      json_serializable: ^4.1.3
    

    Затем я создал отдельный файл json_demo.dart и взял часть вашего кода. Еще я добавил несколько вещей:

    1. часть инструкции, которая будет включать сгенерированный файл сопоставления json
    2. Для каждого класса добавлен именованный конструктор .fromJson
    3. Для каждого класса добавлен метод Json ().
    4. Добавлен @JsonSerializable () для каждого класса — так инструмент знает, на какой класс смотреть
    5. Добавлены @JsonKey (name: ‘poster_profile_pic’) и @JsonKey (name: ‘poster_username’) — поскольку ваши имена полей отличаются от имен свойств Json (poster_profile_pic vs posterProfilePic), вам нужно указать инструменту, как переименовать его взад и вперед .
    6. Сделал все свойства обнуляемыми (поскольку я использую последнюю версию с нулевой безопасностью)
    import 'package:json_annotation/json_annotation.dart';
    
    part 'json_demo.g.dart';
    
    @JsonSerializable()
    class VideoComments {
      int? id;
      String? comment;
      int? uid;
      int? likes;
      bool? isLikedByUser;
    
      @JsonKey(name: 'poster_profile_pic')
      String? posterProfilePic;
    
      @JsonKey(name: 'poster_username')
      String? posterUsername;
      List<Responses>? responses;
    
      VideoComments(
          {this.id,
          this.comment,
          this.uid,
          this.likes,
          this.isLikedByUser,
          this.posterProfilePic,
          this.posterUsername,
          this.responses});
    
      factory VideoComments.fromJson(Map<String, dynamic> json) => _$VideoCommentsFromJson(json);
      Map<String, dynamic> toJson() => _$VideoCommentsToJson(this);
    }
    
    @JsonSerializable()
    class Responses {
      int? id;
      String? comment;
      int? uid;
      int? likes;
      bool? isLikedByUser;
    
      Responses({this.id, this.comment, this.uid, this.likes, this.isLikedByUser});
    
      factory Responses.fromJson(Map<String, dynamic> json) => _$ResponsesFromJson(json);
      Map<String, dynamic> toJson() => _$ResponsesToJson(this);
    }
    
    

    Теперь просто запустите flutter pub run build_runner build, и вы получите сгенерированный файл json_demo.g.dart:

    // GENERATED CODE - DO NOT MODIFY BY HAND
    
    part of 'json_demo.dart';
    
    // **************************************************************************
    // JsonSerializableGenerator
    // **************************************************************************
    
    VideoComments _$VideoCommentsFromJson(Map<String, dynamic> json) {
      return VideoComments(
        id: json['id'] as int?,
        comment: json['comment'] as String?,
        uid: json['uid'] as int?,
        likes: json['likes'] as int?,
        isLikedByUser: json['isLikedByUser'] as bool?,
        posterProfilePic: json['poster_profile_pic'] as String?,
        posterUsername: json['poster_username'] as String?,
        responses: (json['responses'] as List<dynamic>?)
            ?.map((e) => Responses.fromJson(e as Map<String, dynamic>))
            .toList(),
      );
    }
    
    Map<String, dynamic> _$VideoCommentsToJson(VideoComments instance) =>
        <String, dynamic>{
          'id': instance.id,
          'comment': instance.comment,
          'uid': instance.uid,
          'likes': instance.likes,
          'isLikedByUser': instance.isLikedByUser,
          'poster_profile_pic': instance.posterProfilePic,
          'poster_username': instance.posterUsername,
          'responses': instance.responses,
        };
    
    Responses _$ResponsesFromJson(Map<String, dynamic> json) {
      return Responses(
        id: json['id'] as int?,
        comment: json['comment'] as String?,
        uid: json['uid'] as int?,
        likes: json['likes'] as int?,
        isLikedByUser: json['isLikedByUser'] as bool?,
      );
    }
    
    Map<String, dynamic> _$ResponsesToJson(Responses instance) => <String, dynamic>{
          'id': instance.id,
          'comment': instance.comment,
          'uid': instance.uid,
          'likes': instance.likes,
          'isLikedByUser': instance.isLikedByUser,
        };
    
    

    Обратите внимание, как он правильно переименовал свойство json на основе аннотации:

    posterProfilePic: json['poster_profile_pic'] as String?,
    

    Еще одна вещь, на которую следует обратить внимание — вот как он решил вашу проблему:

    responses: (json['responses'] as List<dynamic>?)
            ?.map((e) => Responses.fromJson(e as Map<String, dynamic>))
            .toList(),
    

    С этого момента каждый раз, когда вы меняете свой класс, просто повторно запускайте скрипт сборки.

    По мере того, как вы исследуете дальше, вы увидите, что он может обрабатывать перечисления, у него есть хорошие аннотации для автоматического переименования всех полей в классе из корпуса змеи в ящик для кебаба (или как он там называется). Но самое главное — делает это последовательно и проверенно. Если вы добавите больше классов, это действительно поможет вам сэкономить время …

    А чтобы облегчить себе жизнь, создайте пользовательский сниппет в VS Code (Файл- ›Настройки-› Пользовательские сниппеты):

    {
        "Json Serializible": {
            "prefix": "serial",
            "body": [
                "factory ${1}.fromJson(Map<String, dynamic> json) => _$${1}FromJson(json);",
                "Map<String, dynamic> toJson() => _$${1}ToJson(this);"
            ]
        }
    }
    

    Вот как это использовать:

    1. Скопируйте название вашего класса
    2. начните вводить «серийный номер» — это ярлык для фрагмента кода
    3. Выберите фрагмент и вставьте имя класса — он вставит его 3 раза, где это необходимо.

    Смотрите — проще, чем научиться вручную кодировать / декодировать Json ….

    большое вам спасибо за это. Один из лучших ответов, с которыми я сталкивался за последнее время, и, как вы сказали, он объясняет, насколько это может быть просто. Итак, с учетом сказанного, я не буду все переписывать, чтобы начать использовать это! person James P; 23.06.2021

  2. James P

    Как предположил @Andrija, это, безусловно, хороший способ, если вы не хотите его использовать, тогда используйте расширение vscode bendixma.dart-data-class-generator, создайте свой класс и сохраните свои переменные, просто нажмите ctrl + shift + p — ›Поиск класса данных, нажмите Enter, когда найдете генератор класса данных dart в свойствах класса.

  3. James P

    Всем спасибо за ответы. Я не решил проблему.

    Я изменил свой код (в отношении области проблемы) на следующий:

        if (json['responses'].length > 0) {
          json['responses'].forEach((e) => responses.add(Responses.fromJson(e)));
        } else if (json['responses'].length == 0 || json['responses'] == null) {
          responses = null;
        }
    

    а на своем веб-сервере я заключил ответы в другой (родительский) массив.

  4. James P

    это ошибка, связанная с типом данных. Пожалуйста, скрестите ответ JSON и тип данных переменной класса Dart. Строковая переменная не может быть значением типа Int или Double. Или сделайте что-нибудь, изменив все переменные на динамический тип. Но да, иметь динамический тип данных для всех переменных — не лучшая практика.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: