dart/flutter中json转dart实体类(支持泛型)

实现方式

安装VSCode相关插件

  • Json to Dart Model Enhanced: 用于直接将json生成dart的实体类和json<-->dart实体的转换类
  • Dart build_runner: 根据dart实体类中的json_annotation注解, 生成json<-->dart实体的转换类
  • Build Runner: 和Dart build_runner插件的功能一样

添加依赖

shell 复制代码
dart pub add collection json_annotation freezed_annotation
dart pub add dev:build_runner dev:json_serializable dev:freezed

全自动化生成

复制json字符串 ,vscode的资源管理器的代码目录下,点击右键选择JSON to Dart: From Clipboard to Code Generation Classes, 此时Json to Dart Model Enhanced插件会根据json直接生成带注解的dart实体类,然后再通过build_runner生成xx.g.dart的转换类

示例:

  • 以如下json为例,先复制到剪贴板(ctrl+c)
json 复制代码
{
    "keyword": "果汁茶饮料",
    "branch_code": "4402    ",
    "gtin": "06971196385407",
    "f_id": "QfDJ8I345ukI52xOhiXQdgY5FiOWWKmlDtmV1q3f56k5BdULB8kKnuP5ppwZ6MWxU_8h8Gb2KVK4FMzk-WxFdd-aNwo42y1Fn_Q3n_kBSngtZEYFix45VA6cm34w6qg_dbMIXur_3KJdATWIxvfYHs6PcsvEAHJxCQTyFiX9HnExPUAu42pF5GmErKXVWg2Wli0eEh55Uz7zTqbihbSUGZVmUhC",
    "specification": "450毫升",
    "brandid": "4fDJ8I345ukI52xOhiXQdgY5FiO581PvMkpwWjYfjR22qIfwQH1jBedp9AqmncgQAm3qpOc9OXf-ZA7a2PRJI8qnmCQxJwKvMhg0D90NpT4SZnIxrFis7WrA_UwLyh0qO9XQ_1B5JC17gOA4z7CcE8mCW8nNn06hf6ZhxBzOL37_weYrnYff5-Qe6FuKXRVxPVfW4r0aHWVLlUtz-Nt0Yah2nhC",
    "base_id": "AfDJ8I345ukI52xOhiXQdgY5FiMd64Ylky-sPivS6c5hBN1BK14Ya6Ck5jZ8uZF28imXdD-p96fJHbMxjQYMU02VtainJV_QZesabEt4oo68GnZxXoC3FVpmUY7p168F_QyIqRmRFNQ3jmAIduTj0Mpr5ccJ5cvUvO7UTc-WoqJqjn56rrOGZjtMCL1L2d92xE6r8aTn_i6ktSjtSQmQYFGPiBC",
    "is_private": false,
    "firm_name": "深圳市品道餐饮管理有限公司",
    "brandcn": "奈雪的茶",
    "picture_filename": "/userfile/2023224/593945533.png",
    "description": "奈雪柠檬茶鸭屎香柠檬茶(果汁茶饮料)",
    "logout_flag": "0",
    "have_ms_product": 0,
    "base_create_time": "2022-07-20T09:58:10.8",
    "branch_name": "深圳分中心",
    "base_source": "WWW",
    "gpc": "10000313",
    "gpcname": "液体/即饮型水果花草混合茶",
    "saledate": "2023-02-24T00:00:00",
    "saledateyear": 2023,
    "base_last_updated": "2023-05-04T23:36:26.137",
    "base_user_id": "1563092",
    "code": "697119638",
    "levels": null,
    "levels_source": null,
    "valid_date": "2029-08-01T00:00:00",
    "logout_date": null,
    "gtinstatus": 1
}

在vscode的资源管理器的代码目录下,点击右键选择JSON to Dart: From Clipboard to Code Generation Classes

此时Json to Dart Model Enhanced会询问一系列问题,如:实体类名,是否生成xx方法,所有东西填完之后,会生成一个目录和两个文件

goods.dart中是dart的实体类定义

dart 复制代码
import 'package:collection/collection.dart';
import 'package:json_annotation/json_annotation.dart';

part 'goods.g.dart';

@JsonSerializable()
class Goods {
	String? keyword;
	@JsonKey(name: 'branch_code') 
	String? branchCode;
	String? gtin;
	@JsonKey(name: 'f_id') 
	String? fId;
	String? specification;
	String? brandid;
	@JsonKey(name: 'base_id') 
	String? baseId;
	@JsonKey(name: 'is_private') 
	bool? isPrivate;
	@JsonKey(name: 'firm_name') 
	String? firmName;
	String? brandcn;
	@JsonKey(name: 'picture_filename') 
	String? pictureFilename;
	String? description;
	@JsonKey(name: 'logout_flag') 
	String? logoutFlag;
	@JsonKey(name: 'have_ms_product') 
	num? haveMsProduct;
	@JsonKey(name: 'base_create_time') 
	DateTime? baseCreateTime;
	@JsonKey(name: 'branch_name') 
	String? branchName;
	@JsonKey(name: 'base_source') 
	String? baseSource;
	String? gpc;
	String? gpcname;
	String? saledate;
	num? saledateyear;
	@JsonKey(name: 'base_last_updated') 
	DateTime? baseLastUpdated;
	@JsonKey(name: 'base_user_id') 
	String? baseUserId;
	String? code;
	dynamic levels;
	@JsonKey(name: 'levels_source') 
	dynamic levelsSource;
	@JsonKey(name: 'valid_date') 
	String? validDate;
	@JsonKey(name: 'logout_date') 
	dynamic logoutDate;
	num? gtinstatus;

	Goods({
		this.keyword, 
		this.branchCode, 
		this.gtin, 
		this.fId, 
		this.specification, 
		this.brandid, 
		this.baseId, 
		this.isPrivate, 
		this.firmName, 
		this.brandcn, 
		this.pictureFilename, 
		this.description, 
		this.logoutFlag, 
		this.haveMsProduct, 
		this.baseCreateTime, 
		this.branchName, 
		this.baseSource, 
		this.gpc, 
		this.gpcname, 
		this.saledate, 
		this.saledateyear, 
		this.baseLastUpdated, 
		this.baseUserId, 
		this.code, 
		this.levels, 
		this.levelsSource, 
		this.validDate, 
		this.logoutDate, 
		this.gtinstatus, 
	});

	@override
	String toString() {
		return 'Goods(keyword: $keyword, branchCode: $branchCode, gtin: $gtin, fId: $fId, specification: $specification, brandid: $brandid, baseId: $baseId, isPrivate: $isPrivate, firmName: $firmName, brandcn: $brandcn, pictureFilename: $pictureFilename, description: $description, logoutFlag: $logoutFlag, haveMsProduct: $haveMsProduct, baseCreateTime: $baseCreateTime, branchName: $branchName, baseSource: $baseSource, gpc: $gpc, gpcname: $gpcname, saledate: $saledate, saledateyear: $saledateyear, baseLastUpdated: $baseLastUpdated, baseUserId: $baseUserId, code: $code, levels: $levels, levelsSource: $levelsSource, validDate: $validDate, logoutDate: $logoutDate, gtinstatus: $gtinstatus)';
	}

	factory Goods.fromJson(Map<String, dynamic> json) => _$GoodsFromJson(json);

	Map<String, dynamic> toJson() => _$GoodsToJson(this);

	Goods copyWith({
		String? keyword,
		String? branchCode,
		String? gtin,
		String? fId,
		String? specification,
		String? brandid,
		String? baseId,
		bool? isPrivate,
		String? firmName,
		String? brandcn,
		String? pictureFilename,
		String? description,
		String? logoutFlag,
		num? haveMsProduct,
		DateTime? baseCreateTime,
		String? branchName,
		String? baseSource,
		String? gpc,
		String? gpcname,
		String? saledate,
		num? saledateyear,
		DateTime? baseLastUpdated,
		String? baseUserId,
		String? code,
		dynamic levels,
		dynamic levelsSource,
		String? validDate,
		dynamic logoutDate,
		num? gtinstatus,
	}) {
		return Goods(
			keyword: keyword ?? this.keyword,
			branchCode: branchCode ?? this.branchCode,
			gtin: gtin ?? this.gtin,
			fId: fId ?? this.fId,
			specification: specification ?? this.specification,
			brandid: brandid ?? this.brandid,
			baseId: baseId ?? this.baseId,
			isPrivate: isPrivate ?? this.isPrivate,
			firmName: firmName ?? this.firmName,
			brandcn: brandcn ?? this.brandcn,
			pictureFilename: pictureFilename ?? this.pictureFilename,
			description: description ?? this.description,
			logoutFlag: logoutFlag ?? this.logoutFlag,
			haveMsProduct: haveMsProduct ?? this.haveMsProduct,
			baseCreateTime: baseCreateTime ?? this.baseCreateTime,
			branchName: branchName ?? this.branchName,
			baseSource: baseSource ?? this.baseSource,
			gpc: gpc ?? this.gpc,
			gpcname: gpcname ?? this.gpcname,
			saledate: saledate ?? this.saledate,
			saledateyear: saledateyear ?? this.saledateyear,
			baseLastUpdated: baseLastUpdated ?? this.baseLastUpdated,
			baseUserId: baseUserId ?? this.baseUserId,
			code: code ?? this.code,
			levels: levels ?? this.levels,
			levelsSource: levelsSource ?? this.levelsSource,
			validDate: validDate ?? this.validDate,
			logoutDate: logoutDate ?? this.logoutDate,
			gtinstatus: gtinstatus ?? this.gtinstatus,
		);
	}

	@override
	bool operator ==(Object other) {
		if (identical(other, this)) return true;
		if (other is! Goods) return false;
		final mapEquals = const DeepCollectionEquality().equals;
		return mapEquals(other.toJson(), toJson());
	}

	@override
	int get hashCode =>
			keyword.hashCode ^
			branchCode.hashCode ^
			gtin.hashCode ^
			fId.hashCode ^
			specification.hashCode ^
			brandid.hashCode ^
			baseId.hashCode ^
			isPrivate.hashCode ^
			firmName.hashCode ^
			brandcn.hashCode ^
			pictureFilename.hashCode ^
			description.hashCode ^
			logoutFlag.hashCode ^
			haveMsProduct.hashCode ^
			baseCreateTime.hashCode ^
			branchName.hashCode ^
			baseSource.hashCode ^
			gpc.hashCode ^
			gpcname.hashCode ^
			saledate.hashCode ^
			saledateyear.hashCode ^
			baseLastUpdated.hashCode ^
			baseUserId.hashCode ^
			code.hashCode ^
			levels.hashCode ^
			levelsSource.hashCode ^
			validDate.hashCode ^
			logoutDate.hashCode ^
			gtinstatus.hashCode;
}

goods.g.dart中是dart的json<-->dart实体类的转换逻辑实现

dart 复制代码
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'goods.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Goods _$GoodsFromJson(Map<String, dynamic> json) => Goods(
      keyword: json['keyword'] as String?,
      branchCode: json['branch_code'] as String?,
      gtin: json['gtin'] as String?,
      fId: json['f_id'] as String?,
      specification: json['specification'] as String?,
      brandid: json['brandid'] as String?,
      baseId: json['base_id'] as String?,
      isPrivate: json['is_private'] as bool?,
      firmName: json['firm_name'] as String?,
      brandcn: json['brandcn'] as String?,
      pictureFilename: json['picture_filename'] as String?,
      description: json['description'] as String?,
      logoutFlag: json['logout_flag'] as String?,
      haveMsProduct: json['have_ms_product'] as num?,
      baseCreateTime: json['base_create_time'] == null
          ? null
          : DateTime.parse(json['base_create_time'] as String),
      branchName: json['branch_name'] as String?,
      baseSource: json['base_source'] as String?,
      gpc: json['gpc'] as String?,
      gpcname: json['gpcname'] as String?,
      saledate: json['saledate'] as String?,
      saledateyear: json['saledateyear'] as num?,
      baseLastUpdated: json['base_last_updated'] == null
          ? null
          : DateTime.parse(json['base_last_updated'] as String),
      baseUserId: json['base_user_id'] as String?,
      code: json['code'] as String?,
      levels: json['levels'],
      levelsSource: json['levels_source'],
      validDate: json['valid_date'] as String?,
      logoutDate: json['logout_date'],
      gtinstatus: json['gtinstatus'] as num?,
    );

Map<String, dynamic> _$GoodsToJson(Goods instance) => <String, dynamic>{
      'keyword': instance.keyword,
      'branch_code': instance.branchCode,
      'gtin': instance.gtin,
      'f_id': instance.fId,
      'specification': instance.specification,
      'brandid': instance.brandid,
      'base_id': instance.baseId,
      'is_private': instance.isPrivate,
      'firm_name': instance.firmName,
      'brandcn': instance.brandcn,
      'picture_filename': instance.pictureFilename,
      'description': instance.description,
      'logout_flag': instance.logoutFlag,
      'have_ms_product': instance.haveMsProduct,
      'base_create_time': instance.baseCreateTime?.toIso8601String(),
      'branch_name': instance.branchName,
      'base_source': instance.baseSource,
      'gpc': instance.gpc,
      'gpcname': instance.gpcname,
      'saledate': instance.saledate,
      'saledateyear': instance.saledateyear,
      'base_last_updated': instance.baseLastUpdated?.toIso8601String(),
      'base_user_id': instance.baseUserId,
      'code': instance.code,
      'levels': instance.levels,
      'levels_source': instance.levelsSource,
      'valid_date': instance.validDate,
      'logout_date': instance.logoutDate,
      'gtinstatus': instance.gtinstatus,
    };

通过定义带注解的实体类,手动生成xx.g.dart文件

看到这里可能你会有疑问,都能自动生成了,还手写干嘛?

  • json中可能缺少部分字段
  • 可能生成的实体类字段类型不完全正确
  • 全自动生成方式,无法使用泛型

手动定义支持泛型的实体类,再自动生成xx.g.dart文件

手动定义支持泛型的实体类

base_entity.dart

dart 复制代码
import 'package:json_annotation/json_annotation.dart';

// 这个文件即使还没有,也先写上,自动生成的转换逻辑文件,就是这种命名风格
part 'base_bean.g.dart';

//genericArgumentFactories以支持泛型
@JsonSerializable(genericArgumentFactories: true)
class BaseBean<T> {
  @JsonKey(name: 'code')
  int? code;
  @JsonKey(name: 'data')
  T? data;

  BaseBean({this.data, this.code});

  factory BaseBean.fromJson(
          Map<String, dynamic> json, T Function(dynamic json) fromJsonT) =>
      // 这个方法即使还没有(方法出自base_bean.g.dart文件),也写上
      _$BaseBeanFromJson(json, fromJsonT);

  Map<String, dynamic> toJson(Object? Function(T value) toJsonT) =>
      // 这个方法即使还没有(方法出自base_bean.g.dart文件),也写上
      _$BaseBeanToJson(this, toJsonT);
}

生成xx.g.dart文件

方式一: 通过Dart build_runner插件的界面点击生成

方式二: 通过Build Runner插件的快捷键生成

P.S. 需要先将光标定位到VSCode的资源管理器的任意一个目录

ctrl + shift + b

生成的效果:base_bean.g.dart

dart 复制代码
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'base_bean.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

BaseBean<T> _$BaseBeanFromJson<T>(
  Map<String, dynamic> json,
  T Function(Object? json) fromJsonT,
) =>
    BaseBean<T>(
      data: _$nullableGenericFromJson(json['data'], fromJsonT),
      code: json['code'] as int?,
    );

Map<String, dynamic> _$BaseBeanToJson<T>(
  BaseBean<T> instance,
  Object? Function(T value) toJsonT,
) =>
    <String, dynamic>{
      'code': instance.code,
      'data': _$nullableGenericToJson(instance.data, toJsonT),
    };

T? _$nullableGenericFromJson<T>(
  Object? input,
  T Function(Object? json) fromJson,
) =>
    input == null ? null : fromJson(input);

Object? _$nullableGenericToJson<T>(
  T? input,
  Object? Function(T value) toJson,
) =>
    input == null ? null : toJson(input);

使用这个泛型实体类

dart 复制代码
import 'dart:convert';

import 'package:demo_01/base_bean/base_bean.dart';
import 'package:demo_01/person/person.dart';

void main() {
  String jsonStr =
      '{"code":1,"data":{"name":"张三","age":12,"male":true,"hobby":["吃饭","睡觉"]}}';
  BaseBean<Person> bean =
      BaseBean.fromJson(jsonDecode(jsonStr), (json) => Person.fromJson(json));
  print(bean.toString());
  print(bean.code);
  print(bean.data?.name);
  print(bean.data?.age);
  print(bean.data?.hobby);
  print(bean.data?.toJson());
}

参考资料

Flutter 使用json_annotation创建数据模型 - 简书 (jianshu.com)

Flutter使用json_serializable泛型化的问题_flutter 序列化 泛型-CSDN博客

Flutter 使用 json_serializable 解析 JSON 支持泛型 - 掘金 (juejin.cn)

Flutter 基于Dio封装网络请求+泛型解析返回数据_flutter http返回值解析-CSDN博客

lib/generated/json/base/json_convert_content.dart · 江沨/flutter_demo - 码云 - 开源中国 (gitee.com)

如何在Dart/Flutter中解析JSON的基本指南 - 掘金 (juejin.cn)

相关推荐
AiFlutter15 小时前
Flutter之Package教程
flutter
Mingyueyixi20 小时前
Flutter Spacer引发的The ParentDataWidget Expanded(flex: 1) 惨案
前端·flutter
crasowas1 天前
Flutter问题记录 - 适配Xcode 16和iOS 18
flutter·ios·xcode
老田低代码2 天前
Dart自从引入null check后写Flutter App总有一种难受的感觉
前端·flutter
AiFlutter3 天前
Flutter Web首次加载时添加动画
前端·flutter
ZemanZhang4 天前
Flutter启动无法运行热重载
flutter
AiFlutter4 天前
Flutter-底部选择弹窗(showModalBottomSheet)
flutter
帅次5 天前
Android Studio:驱动高效开发的全方位智能平台
android·ide·flutter·kotlin·gradle·android studio·android jetpack
程序者王大川5 天前
【前端】Flutter vs uni-app:性能对比分析
前端·flutter·uni-app·安卓·全栈·性能分析·原生
yang2952423615 天前
使用 Vue.js 将数据对象的值放入另一个数据对象中
前端·vue.js·flutter