有没有一种惯用的方法可以将JSON解析为具有共享基类但数据形状不同的对象



我是Dart的新手,所以我一直在为现有的API实现SDK,但我遇到了一些问题,弄清楚如何构建东西来减少样板我正在分析来自服务器的响应。

基本上,来自API的所有响应如下所示:

{
"data": {
...
},
"error": {
...
}
}

其中存在dataerror,但决不能同时存在两者。相当标准的东西。error并不那么有趣,因为它对每个响应都是一样的,但data对象每个响应都不同。到目前为止,我已经能够得到一些";工作";通过声明基类处理错误并通过super向上传递(我应该注意,我正在使用json_serializer包(:

class Response {
Response({this.error});
Error? error;
}
@JsonSerializable(createToJson: false)
class ExampleResponse extends Response {
ExampleResponse({
this.data,
Error? error,
}) : super(error: error);
Map<String, dynamic>? data;
factory ExampleResponse.fromJson(Map<String, dynamic> json) =>
_$ExampleResponseFromJson(json);
}

这是有效的,但我丢失了data字段的所有类型信息。我不知道该怎么办从这里开始,它不涉及数据形状的另一个类:

@JsonSerializable()
class Thing {
Thing(this.name);
String name;
factory Thing.fromJson(Map<String, dynamic> json) => _$ThingFromJson(json);
String toString() => 'Thing{name: $name}';
}
@JsonSerializable()
class ExampleResponseData {
ExampleResponseData(this.thing);
Thing thing;
factory ExampleResponseData.fromJson(Map<String, dynamic> json) =>
_$ExampleResponseDataFromJson(json);
String toString() => 'ExampleResponseData{thing: $thing}';
}
@JsonSerializable(createToJson: false)
class ExampleResponse extends Response {
ExampleResponse({
this.data,
Error? error,
}) : super(error: error);
ExampleResponseData? data;
factory ExampleResponse.fromJson(Map<String, dynamic> json) =>
_$ExampleResponseFromJson(json);
String toString() => 'ExampleResponse{error: $error, data: $data}';
}

在Go中,我只会为类型在JSON之外没有值的情况定义一个内部结构解析类似:

type ExampleResponse struct {
Error *Error `json:"error"`
Data *struct {
Thing Thing `json:"thing"`
} `json:"thing"`
}

但总体而言,我对OOP更为粗糙,对Dart更为陌生。有没有更好的方法来处理这个问题,不会导致两倍于响应的类型?

您可以找到基于build_value的解决方案。想法是对数据类型使用泛型。构建值可以处理这样的情况,尽管你会有一点样板。下面你可以找到例子:


import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
part 'built_value_example.g.dart';
void main(List<String> arguments) {
final resultAJson = endpointA();
final resultA = serializers.deserialize(resultAJson, specifiedType: FullType(Response, [FullType(DataA)]));
print(resultA);
final resultBJson = endpointB();
final resultB = serializers.deserialize(resultBJson, specifiedType: FullType(Response, [FullType(DataB)]));
print(resultB);
}
Map<String, dynamic> endpointA() {
final data = DataA((b) => b..dataAField = 7);
final response = Response<DataA>((b) => b..data = data);
return serializers.serialize(response, specifiedType: FullType(Response, [FullType(DataA)])) as Map<String, dynamic>;
}
Map<String, dynamic> endpointB() {
final data = DataB((b) => b..dataBField = 'data b value');
final response = Response<DataB>((b) => b..data = data..error = 'data b error');
return serializers.serialize(response, specifiedType: FullType(Response, [FullType(DataB)])) as Map<String, dynamic>;
}
abstract class Response<DATA_TYPE> implements Built<Response<DATA_TYPE>, ResponseBuilder<DATA_TYPE>> {
Response._();
factory Response([Function(ResponseBuilder<DATA_TYPE> b) updates]) = _$Response<DATA_TYPE>;
static Serializer<Response> get serializer => _$responseSerializer;
DATA_TYPE get data;
String? get error;
}
abstract class DataA implements Built<DataA, DataABuilder> {
DataA._();
factory DataA([Function(DataABuilder b) updates]) = _$DataA;
static Serializer<DataA> get serializer => _$dataASerializer;
int get dataAField;
}
abstract class DataB implements Built<DataB, DataBBuilder> {
DataB._();
factory DataB([Function(DataBBuilder b) updates]) = _$DataB;
static Serializer<DataB> get serializer => _$dataBSerializer;
String get dataBField;
}
@SerializersFor([
DataA,
DataB,
Response,
])
final Serializers serializers = (_$serializers.toBuilder()
..addPlugin(StandardJsonPlugin())
..addBuilderFactory(FullType(Response, [FullType(DataA)]), () => ResponseBuilder<DataA>())
..addBuilderFactory(FullType(Response, [FullType(DataB)]), () => ResponseBuilder<DataB>()))
.build();

最新更新