刚接触Dart,发请求返回json,进行序列化,官方给了方案,也给了推荐的工具,json_serializable。但是这个工具真的有坑!记录一下。
数据结构如下:
{"jsonrpc":"2.0","result":"123","id":0}
{"jsonrpc":"2.0","error":{"code":-1,"message":"error"},"id":0}
交易成功没有error,result可能是不同的结构,其他结构不变,所以需要用到泛型。
json_serializable操作过程请看自行看flutter网或者github。
出来的结果是这样的。
import 'package:json_annotation/json_annotation.dart';
part 'response.g.dart';
class Response<T> {
Response(this.id, this.jsonrpc, this.result, this.error);
int id;
String jsonrpc;
T result;
Error error;
}
@JsonSerializable()
class Error {
Error(this.code, this.message, this.data);
int code;
String message;
String data;
factory Error.fromJson(Map<String, dynamic> json) => _$ErrorFromJson(json);
Map<String, dynamic> toJson() => _$ErrorToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'response.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Error _$ErrorFromJson(Map<String, dynamic> json) {
return Error(
json['code'] as int, json['message'] as String, json['data'] as String);
}
Map<String, dynamic> _$ErrorToJson(Error instance) => <String, dynamic>{
'code': instance.code,
'message': instance.message,
'data': instance.data
};
import './response.dart';
import 'package:json_annotation/json_annotation.dart';
part 'son.g.dart';
@JsonSerializable()
class SonRes extends Response<String> {
SonRes(id, jsonrpc, result, error) : super(id, jsonrpc, result, error);
getBlockHash() {
return result;
}
factory SonRes.fromJson(Map<String, dynamic> json) => _$SonResFromJson(json);
Map<String, dynamic> toJson() => _$SonResToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'son.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
SonRes _$SonResFromJson(Map<String, dynamic> json) {
return SonRes(json['id'], json['jsonrpc'], json['result'], json['error']);
}
Map<String, dynamic> _$SonResToJson(SonRes instance) => <String, dynamic>{
'id': instance.id,
'jsonrpc': instance.jsonrpc,
'result': instance.result,
'error': instance.error
};
实际运行之后,交易成功解析没问题,但是交易失败解析会报错
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Error'
是不是超级坑爹,然而可以很明显的看到是Error类出问题了。
这个工具是为了节省时间,自动生成解析代码,但是其实是有问题的,需要修改。(被误导了)
简单来说就是定义一个fromjson的方法,然后手动去转换。
看这段代码
SonRes _$SonResFromJson(Map<String, dynamic> json) {
return SonRes(json['id'], json['jsonrpc'], json['result'], json['error']);
}
问题出在最后一个参数json[‘error’],我们取出来的其实Map<String, dynamic>这么一个map,但我们也需要将他序列化,所以修改成
SonRes _$SonResFromJson(Map<String, dynamic> json) {
return SonRes(json['id'], json['jsonrpc'], json['result'], Error.fromJson(json['error']));
}
再次运行,失败的时候可以正常解析,但是成功的时候又不可以了,报错:
Unhandled exception:
NoSuchMethodError: The method '[]' was called on null.
Receiver: null
Tried calling: []("code")
原因是成功的时候没有error字段,但是序列化的时候依然会去解析error,导致类似空指针的问题,只能去做一次判断
factory SonRes.fromJson(Map<String, dynamic> json) {
return SonRes(json['id'], json['jsonrpc'], json['result'],
json['error'] == null ? null : Error.fromJson(json['error']));
}
到这儿就可以正常解析了。没必要用part引入,所以直接把代码放一起。去掉工具
完整代码
class Response<T> {
Response(this.id, this.jsonrpc, this.result, this.error);
int id;
String jsonrpc;
T result;
Error error;
}
class Error {
Error(this.code, this.message, this.data);
int code;
String message;
String data;
factory Error.fromJson(Map<String, dynamic> json) {
return Error(
json['code'] as int, json['message'] as String, json['data'] as String);
}
Map<String, dynamic> toJson() => <String, dynamic>{
'code': this.code,
'message': this.message,
'data': this.data
};
}
import './response.dart';
class SonRes extends Response<String> {
SonRes(id, jsonrpc, result, error) : super(id, jsonrpc, result, error);
getBlockHash() {
return result;
}
factory SonRes.fromJson(Map<String, dynamic> json) {
return SonRes(json['id'], json['jsonrpc'], json['result'],
json['error'] == null ? null : Error.fromJson(json['error']));
}
Map<String, dynamic> toJson() => <String, dynamic>{
'id': this.id,
'jsonrpc': this.jsonrpc,
'result': this.result,
'error': this.error
};
}
和java序列化比起来,真的是太难用了。