如何在Dart中避免互联对象JSON序列化时的StackOverflow错误?
解决Dart中Vehicle与Edge循环引用的JSON序列化栈溢出问题
循环引用会导致序列化时无限递归调用toJson方法,最终触发StackOverflow错误。以下是几种可行的解决方法:
方案一:用唯一ID替代对象引用(推荐)
给Vehicle和Edge添加唯一标识符(如String id),序列化时仅存储关联对象的ID,而非整个对象实例,彻底避免循环引用。
修改后的类示例:
class Vehicle { final String id; final String? nextCarId; // 存储后方车辆ID,替代原nextCar final String? previousCarId; // 存储前方车辆ID,替代原previousCar final String? edgeId; // 存储关联Edge的ID,替代原edge Vehicle({ required this.id, this.nextCarId, this.previousCarId, this.edgeId, }); Map<String, dynamic> toJson() { return { 'id': id, 'nextCarId': nextCarId, 'previousCarId': previousCarId, 'edgeId': edgeId, }; } // 反序列化时借助缓存字典重建对象引用 factory Vehicle.fromJson(Map<String, dynamic> json, Map<String, Vehicle> vehicleCache, Map<String, Edge> edgeCache) { final vehicle = Vehicle( id: json['id'], nextCarId: json['nextCarId'], previousCarId: json['previousCarId'], edgeId: json['edgeId'], ); vehicleCache[vehicle.id] = vehicle; // 后续可根据edgeId从edgeCache获取对应的Edge,补全关联关系 return vehicle; } } class Edge { final String id; final String connectorCarId; // 存储发起连接车辆的ID,替代原connectorCar final String connectedCarId; // 存储被连接车辆的ID,替代原connectedCar Edge({ required this.id, required this.connectorCarId, required this.connectedCarId, }); Map<String, dynamic> toJson() { return { 'id': id, 'connectorCarId': connectorCarId, 'connectedCarId': connectedCarId, }; } factory Edge.fromJson(Map<String, dynamic> json) { return Edge( id: json['id'], connectorCarId: json['connectorCarId'], connectedCarId: json['connectedCarId'], ); } }
这种方式生成的JSON结构简洁无冗余,反序列化时通过缓存字典能快速重建对象间的引用关系。
方案二:自定义序列化逻辑,仅序列化核心信息
如果不想修改类的关联结构,可以在toJson方法中手动控制序列化内容,只输出对象的核心属性和关联对象的ID(而非完整对象),避免递归调用。
示例代码:
class Vehicle { final String id; final Vehicle? nextCar; final Vehicle? previousCar; final Edge? edge; Vehicle({required this.id, this.nextCar, this.previousCar, this.edge}); Map<String, dynamic> toJson() { return { 'id': id, // 仅序列化关联对象的ID,而非整个实例 'nextCarId': nextCar?.id, 'previousCarId': previousCar?.id, 'edgeId': edge?.id, }; } } class Edge { final String id; final Vehicle connectorCar; final Vehicle connectedCar; Edge({required this.id, required this.connectorCar, required this.connectedCar}); Map<String, dynamic> toJson() { return { 'id': id, 'connectorCarId': connectorCar.id, 'connectedCarId': connectedCar.id, }; } }
这种方式无需大幅改动原有类结构,但反序列化时仍需借助缓存来重建对象间的引用。
方案三:结合序列化库处理循环引用
如果使用json_serializable这类代码生成库,可以通过自定义转换器实现ID与对象的转换,减少手动编写序列化逻辑的工作量。
首先定义转换器:
// 全局缓存,用于反序列化时查找对象 final Map<String, Vehicle> vehicleCache = {}; final Map<String, Edge> edgeCache = {}; class VehicleIdConverter implements JsonConverter<Vehicle?, String?> { const VehicleIdConverter(); @override Vehicle? fromJson(String? json) { return json != null ? vehicleCache[json] : null; } @override String? toJson(Vehicle? object) { return object?.id; } } class EdgeIdConverter implements JsonConverter<Edge?, String?> { const EdgeIdConverter(); @override Edge? fromJson(String? json) { return json != null ? edgeCache[json] : null; } @override String? toJson(Edge? object) { return object?.id; } }
然后在类中使用转换器:
import 'package:json_annotation/json_annotation.dart'; part 'vehicle.g.dart'; @JsonSerializable() class Vehicle { final String id; @VehicleIdConverter() final Vehicle? nextCar; @VehicleIdConverter() final Vehicle? previousCar; @EdgeIdConverter() final Edge? edge; Vehicle({required this.id, this.nextCar, this.previousCar, this.edge}); factory Vehicle.fromJson(Map<String, dynamic> json) { final vehicle = _$VehicleFromJson(json); vehicleCache[vehicle.id] = vehicle; return vehicle; } Map<String, dynamic> toJson() => _$VehicleToJson(json); }
这种方式结合了代码生成的便利性,同时解决了循环引用问题。
内容的提问来源于stack exchange,提问作者Harold Finch
相关产品推荐
相关产品推荐

