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

同一WebSocket端点不同JSON响应转Dart类的最优方案

基于WebSocket多类型JSON转换为Dart类的最优方案

针对你遇到的WebSocket返回不同type对应不同结构responseData的场景,最优方案是通过泛型基础类+分类型实体模型+分支解析的方式实现,既保证类型安全,又能灵活扩展。

1. 定义泛型基础响应类

先封装一个通用的WebSocket响应类,用泛型兼容不同类型的responseData:

class WebSocketResponse<T> {
  final int type;
  final List<T> responseData;

  WebSocketResponse({required this.type, required this.responseData});

  // 通用构造函数,传入具体类型的解析方法
  factory WebSocketResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Map<String, dynamic>) itemParser,
  ) {
    return WebSocketResponse(
      type: json['type'] as int,
      responseData: (json['responseData'] as List)
          .map((item) => itemParser(item as Map<String, dynamic>))
          .toList(),
    );
  }
}

2. 为每个type创建对应的数据实体类

根据不同type的responseData结构,分别定义强类型的Dart类:

// Type 1 对应的相册模型
class Album {
  final int albumId;
  final int id;
  final String title;
  final String url;

  Album({
    required this.albumId,
    required this.id,
    required this.title,
    required this.url,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      albumId: json['albumId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
      url: json['url'] as String,
    );
  }
}

// Type 2 对应的品牌模型
class Brand {
  final String name;
  final String logoUrl;

  Brand({required this.name, required this.logoUrl});

  factory Brand.fromJson(Map<String, dynamic> json) {
    return Brand(
      name: json['name'] as String,
      logoUrl: json['logoUrl'] as String,
    );
  }
}

// Type 3 对应的商品模型
class Product {
  final String name;
  final int price;

  Product({required this.name, required this.price});

  factory Product.fromJson(Map<String, dynamic> json) {
    return Product(
      name: json['name'] as String,
      price: json['price'] as int,
    );
  }
}

3. 在WebSocket消息处理中分支解析

接收到WebSocket消息后,先解析出type字段,再根据type选择对应的实体类解析:

import 'dart:convert';

void handleWebSocketMessage(String message) {
  final jsonMap = jsonDecode(message) as Map<String, dynamic>;
  final int type = jsonMap['type'] as int;

  switch (type) {
    case 1:
      final albumResponse = WebSocketResponse<Album>.fromJson(
        jsonMap,
        Album.fromJson,
      );
      // 直接使用强类型的albumResponse.responseData处理业务
      break;
    case 2:
      final brandResponse = WebSocketResponse<Brand>.fromJson(
        jsonMap,
        Brand.fromJson,
      );
      // 处理品牌数据
      break;
    case 3:
      final productResponse = WebSocketResponse<Product>.fromJson(
        jsonMap,
        Product.fromJson,
      );
      // 处理商品数据
      break;
    default:
      // 处理未知类型消息,比如打印日志或抛出异常
      break;
  }
}

4. 进阶优化:用代码生成简化序列化

如果觉得手动编写fromJson函数繁琐,可以用freezed+json_serializable库自动生成序列化代码,减少重复工作量:

  1. 在pubspec.yaml添加依赖:
dependencies:
  freezed_annotation: ^2.4.1
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.6
  freezed: ^2.4.5
  json_serializable: ^6.7.1
  1. 用freezed定义模型(以Album为例):
import 'package:freezed_annotation/freezed_annotation.dart';

part 'models.freezed.dart';
part 'models.g.dart';

@freezed
class WebSocketResponse<T> with _$WebSocketResponse<T> {
  const factory WebSocketResponse({
    required int type,
    required List<T> responseData,
  }) = _WebSocketResponse<T>;

  factory WebSocketResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object?) fromJsonT,
  ) => _$WebSocketResponseFromJson(json, fromJsonT);
}

@freezed
class Album with _$Album {
  const factory Album({
    required int albumId,
    required int id,
    required String title,
    required String url,
  }) = _Album;

  factory Album.fromJson(Map<String, dynamic> json) => _$AlbumFromJson(json);
}
  1. 运行命令生成代码:
flutter pub run build_runner build

后续新增type时,只需添加对应的freezed模型即可,无需手动编写序列化逻辑。


内容的提问来源于stack exchange,提问作者dma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:55:37