json_serializable - 将通用字段添加到 freezed/json_serializable class
json_serializable - Add a generic field to a freezed/json_serializable class
如何使 Freezed 对象采用通用类型?我想这样做:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/src/entity_types/option_entity.dart';
part 'vegan_item_tag.freezed.dart';
part 'vegan_item_tag.g.dart';
@freezed
abstract class VeganItemTag<T>
with _$VeganItemTag<T>
implements OptionEntity<T> {
const factory VeganItemTag({int? iconCodePoint, T? id, String? name}) =
_VeganItemTag;
const VeganItemTag._();
factory VeganItemTag.fromJson(Map<String, dynamic> json) =>
_$VeganItemTagFromJson(json);
}
我尝试使用文档中的 @With.fromString('AdministrativeArea<House>')
,但无法将其正确应用到此 class。
错误之一:
lib/src/common/enums/tags/common/vegan_item_tag.freezed.dart:142:32:
Error: Too few positional arguments: 2 required, 1 given.
$$_VeganItemTagFromJson(json);
认为我可能在正确的轨道上,但它不再生成 vegan_item_tag.g.dart
文件:
@freezed
abstract class VeganItemTag<T>
with _$VeganItemTag<T>
implements OptionEntity<T> {
const factory VeganItemTag(
{required int iconCodePoint,
required T id,
required String name}) = _VeganItemTag;
const VeganItemTag._();
factory VeganItemTag.fromJson(
Map<String, Object?> json,
T Function(Object?) fromJsonT,
) => VeganItemTag(
iconCodePoint: json['iconCodePoint'] as int,
id: fromJsonT(json['id']),
name: json['name'] as String,
);
}
您最后的代码没有生成 vegan_item_tag.g.dart
,因为您在 VeganItemTag.fromJson
工厂中编写了错误的代码。将它编辑成这样:
factory VeganItemTag.fromJson(
Map<String, Object?> json,
T Function(Object?) fromJsonT,
) => _$VeganItemTagFromJson(json, fromJsonT);
或者:
factory VeganItemTag.fromJson(Map<String, Object?> json) =>
_$VeganItemTagFromJson(json);
然后重新运行 flutter pub run build_runner build --delete-conflicting-outputs
命令。
这个问题有几种解决方法。但是在所有这些中,您需要明确地将 classes 转换为 Firebase 可以处理的通用类型,例如 String
或 Map<dynamic, String>
.
实现此类行为的 3 种方法是:
FromJson ToJson
在复杂情况下,这比 JsonConverters
更难维护,因此我会放弃此选项作为您的解决方案。
JsonConverters
它适用于通过继承自动转换特定 classes 或抽象 classes,但是从具有不同数据的泛型类型存储它可能不是您所需要的。如果您总是从通用类型 T
中保存相同的值,您可以尝试通过实现的抽象 classes.
使用此解决方案
GenericArgumentFactories
这才是你真正想问的。同时与 genericArgumentFactories on json_serializable and Freezed is not easy and I found a bug on Freezed package 一起工作。
但我设法让这段代码正常工作,这才是真正的解决方案。
@freezed
@JsonSerializable(genericArgumentFactories: true)
class VeganItemTagV2<T> with _$VeganItemTagV2<T> {
const VeganItemTagV2._();
const factory VeganItemTagV2({
required int iconCodePoint,
required T id,
required String name,
}) = _VeganItemTag<T>;
//It only works with block bodies and not with expression bodies
//I don't know why
factory VeganItemTagV2.fromJson(
Map<String, dynamic> json, T Function(Object? json) fromJsonT) {
return _$VeganItemTagV2FromJson<T>(json, fromJsonT);
}
Map<String, dynamic> toJson(Object Function(T value) toJsonT) {
return _$VeganItemTagV2ToJson<T>(this, toJsonT);
}
}
这会根据泛型类型在 toJson
和 fromJson
方法上添加转换器。
注意。这些方法不能是某些错误的表达式,因为它不能编译,但它适用于块体。 Freezed does not oficcially support it 所以你可以考虑创建这个 class 没有冻结包。
这是一个示例,其中封装了 String
的 class 和一个测试 class 以查看其工作原理:
class VeganId {
final String id;
VeganId(this.id);
String itemId() {
return id;
}
@override
String toString() {
return 'VeganId{id: $id}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is VeganId && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
}
测试效果很好
test('veganItemV2 from and toJson', () {
final dto = VeganItemTagV2<VeganId>(
iconCodePoint: 1,
id: VeganId("veganID"),
name: "name",
);
final Map<String, dynamic> actualToJson = dto.toJson((id) => id.itemId());
expect(actualToJson, {"iconCodePoint": 1, "id": "veganID", "name": "name"});
final VeganItemTagV2 actualFromJson = VeganItemTagV2<VeganId>.fromJson(
actualToJson,
(json) =>
VeganId(json as String),
);
expect(actualFromJson, dto);
});
如何使 Freezed 对象采用通用类型?我想这样做:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/src/entity_types/option_entity.dart';
part 'vegan_item_tag.freezed.dart';
part 'vegan_item_tag.g.dart';
@freezed
abstract class VeganItemTag<T>
with _$VeganItemTag<T>
implements OptionEntity<T> {
const factory VeganItemTag({int? iconCodePoint, T? id, String? name}) =
_VeganItemTag;
const VeganItemTag._();
factory VeganItemTag.fromJson(Map<String, dynamic> json) =>
_$VeganItemTagFromJson(json);
}
我尝试使用文档中的 @With.fromString('AdministrativeArea<House>')
,但无法将其正确应用到此 class。
错误之一:
lib/src/common/enums/tags/common/vegan_item_tag.freezed.dart:142:32: Error: Too few positional arguments: 2 required, 1 given.
$$_VeganItemTagFromJson(json);
认为我可能在正确的轨道上,但它不再生成 vegan_item_tag.g.dart
文件:
@freezed
abstract class VeganItemTag<T>
with _$VeganItemTag<T>
implements OptionEntity<T> {
const factory VeganItemTag(
{required int iconCodePoint,
required T id,
required String name}) = _VeganItemTag;
const VeganItemTag._();
factory VeganItemTag.fromJson(
Map<String, Object?> json,
T Function(Object?) fromJsonT,
) => VeganItemTag(
iconCodePoint: json['iconCodePoint'] as int,
id: fromJsonT(json['id']),
name: json['name'] as String,
);
}
您最后的代码没有生成 vegan_item_tag.g.dart
,因为您在 VeganItemTag.fromJson
工厂中编写了错误的代码。将它编辑成这样:
factory VeganItemTag.fromJson(
Map<String, Object?> json,
T Function(Object?) fromJsonT,
) => _$VeganItemTagFromJson(json, fromJsonT);
或者:
factory VeganItemTag.fromJson(Map<String, Object?> json) =>
_$VeganItemTagFromJson(json);
然后重新运行 flutter pub run build_runner build --delete-conflicting-outputs
命令。
这个问题有几种解决方法。但是在所有这些中,您需要明确地将 classes 转换为 Firebase 可以处理的通用类型,例如 String
或 Map<dynamic, String>
.
实现此类行为的 3 种方法是:
FromJson ToJson
在复杂情况下,这比 JsonConverters
更难维护,因此我会放弃此选项作为您的解决方案。
JsonConverters
它适用于通过继承自动转换特定 classes 或抽象 classes,但是从具有不同数据的泛型类型存储它可能不是您所需要的。如果您总是从通用类型 T
中保存相同的值,您可以尝试通过实现的抽象 classes.
GenericArgumentFactories
这才是你真正想问的。同时与 genericArgumentFactories on json_serializable and Freezed is not easy and I found a bug on Freezed package 一起工作。
但我设法让这段代码正常工作,这才是真正的解决方案。
@freezed
@JsonSerializable(genericArgumentFactories: true)
class VeganItemTagV2<T> with _$VeganItemTagV2<T> {
const VeganItemTagV2._();
const factory VeganItemTagV2({
required int iconCodePoint,
required T id,
required String name,
}) = _VeganItemTag<T>;
//It only works with block bodies and not with expression bodies
//I don't know why
factory VeganItemTagV2.fromJson(
Map<String, dynamic> json, T Function(Object? json) fromJsonT) {
return _$VeganItemTagV2FromJson<T>(json, fromJsonT);
}
Map<String, dynamic> toJson(Object Function(T value) toJsonT) {
return _$VeganItemTagV2ToJson<T>(this, toJsonT);
}
}
这会根据泛型类型在 toJson
和 fromJson
方法上添加转换器。
注意。这些方法不能是某些错误的表达式,因为它不能编译,但它适用于块体。 Freezed does not oficcially support it 所以你可以考虑创建这个 class 没有冻结包。
这是一个示例,其中封装了 String
的 class 和一个测试 class 以查看其工作原理:
class VeganId {
final String id;
VeganId(this.id);
String itemId() {
return id;
}
@override
String toString() {
return 'VeganId{id: $id}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is VeganId && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
}
测试效果很好
test('veganItemV2 from and toJson', () {
final dto = VeganItemTagV2<VeganId>(
iconCodePoint: 1,
id: VeganId("veganID"),
name: "name",
);
final Map<String, dynamic> actualToJson = dto.toJson((id) => id.itemId());
expect(actualToJson, {"iconCodePoint": 1, "id": "veganID", "name": "name"});
final VeganItemTagV2 actualFromJson = VeganItemTagV2<VeganId>.fromJson(
actualToJson,
(json) =>
VeganId(json as String),
);
expect(actualFromJson, dto);
});