You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:08:12