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

Flutter中json_serializable包Double类型转换问题及优雅解法问询

解决Dart json_annotation反序列化时字符串转数字的类型错误

问题场景

定义AccountFilter实体类并使用json_annotation生成序列化代码后,反序列化时抛出错误:type 'String' is not a subtype of type 'num' in type cast。原因是接口返回的latitude和longitude字段为字符串类型,但实体类中定义为double,生成的代码直接尝试将字符串强转为num导致失败。

原实体类代码:

import 'package:json_annotation/json_annotation.dart';

part 'account_filter.g.dart';

@JsonSerializable()
class AccountFilter {
  double latitude = 0;
  double longitude = 0;
  String city = "";
  String country = "";
  int minAge = 18;
  int maxAge = 99;
  int gender = 1;

  AccountFilter(
    this.latitude,
    this.longitude,
    this.city,
    this.country,
    this.minAge,
    this.maxAge,
    this.gender,
  );

  factory AccountFilter.fromJson(Map<String, dynamic> json) => _$AccountFilterFromJson(json);

  Map<String, dynamic> toJson() => _$AccountFilterToJson(this);
}

生成的反序列化代码中报错行:

(json['latitude'] as num).toDouble() // 尝试将字符串强转为num失败

优雅解决方案

无需额外定义字符串字段和getter,直接通过json_annotation的扩展能力处理类型转换:

方法一:自定义JsonConverter(复用性高)

创建通用的字符串转double转换器,可在多个字段/类中复用:

class StringToDoubleConverter implements JsonConverter<double, dynamic> {
  const StringToDoubleConverter();

  @override
  double fromJson(dynamic json) {
    if (json is String) {
      return double.parse(json);
    } else if (json is num) {
      return json.toDouble();
    }
    throw FormatException('无法将${json.runtimeType}转换为double');
  }

  @override
  dynamic toJson(double object) => object;
}

在实体类字段上标注转换器:

@JsonSerializable()
class AccountFilter {
  @JsonKey(converter: StringToDoubleConverter())
  double latitude = 0;
  @JsonKey(converter: StringToDoubleConverter())
  double longitude = 0;
  // 其他字段保持不变

  // 构造函数和序列化方法不变
}

方法二:直接在@JsonKey中定义转换逻辑(简洁快速)

如果仅在当前类使用,可直接在@JsonKey参数中写匿名转换函数:

@JsonSerializable()
class AccountFilter {
  @JsonKey(
    fromJson: _stringToDouble,
    toJson: _doubleToString,
  )
  double latitude = 0;
  @JsonKey(
    fromJson: _stringToDouble,
    toJson: _doubleToString,
  )
  double longitude = 0;
  // 其他字段保持不变

  // 构造函数和序列化方法不变

  // 定义转换函数
  static double _stringToDouble(dynamic value) {
    if (value is String) {
      return double.parse(value);
    } else if (value is num) {
      return value.toDouble();
    }
    return 0.0; // 也可根据业务需求抛出异常
  }

  static dynamic _doubleToString(double value) => value;
}

效果

重新执行dart run build_runner build --delete-conflicting-outputs生成代码后,反序列化时会自动兼容字符串/数字类型的latitude和longitude,无需修改业务逻辑,保持实体类字段的类型纯净。

内容的提问来源于stack exchange,提问作者Draško

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 21:45:29