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

Flutter中适配Google Place Details API响应结构,基于JsonSerializable正确生成Place模型及place_model.g.dart的方法

解决JsonSerializable与Google Place Details API Schema不匹配的问题

我来帮你搞定这个问题~你遇到的核心矛盾是:Google Place Details API返回的地址数据嵌套在result.address_components数组里(每个元素带types标识类型),但你的Place模型直接期望平级的street_name等字段,默认的JsonSerializable自动映射根本找不到这些键,所以才会匹配失败。下面是完整的解决方案:

1. 先定义匹配API完整响应的模型类

首先得把API返回的整个JSON结构对应起来,这样才能正确解析出里面的address_components数组。创建三个模型类:

place_details_response.dart

import 'package:json_annotation/json_annotation.dart';
part 'place_details_response.g.dart';

@JsonSerializable(fieldRename: FieldRename.snake)
class PlaceDetailsResponse {
  final List<dynamic> htmlAttributions;
  final PlaceResult result;
  final String status;

  PlaceDetailsResponse({
    required this.htmlAttributions,
    required this.result,
    required this.status,
  });

  factory PlaceDetailsResponse.fromJson(Map<String, dynamic> json) =>
      _$PlaceDetailsResponseFromJson(json);
}

@JsonSerializable(fieldRename: FieldRename.snake)
class PlaceResult {
  final List<AddressComponent> addressComponents;

  PlaceResult({required this.addressComponents});

  factory PlaceResult.fromJson(Map<String, dynamic> json) =>
      _$PlaceResultFromJson(json);
}

@JsonSerializable(fieldRename: FieldRename.snake)
class AddressComponent {
  final String longName;
  final String shortName;
  final List<String> types;

  AddressComponent({
    required this.longName,
    required this.shortName,
    required this.types,
  });

  factory AddressComponent.fromJson(Map<String, dynamic> json) =>
      _$AddressComponentFromJson(json);
}

2. 调整你的Place模型,添加自定义解析逻辑

现在需要从address_components数组里,根据每个元素的types标识提取对应的short_name,填充到Place的字段里。修改你的Place类:

place_model.dart

import 'package:json_annotation/json_annotation.dart';
import 'place_details_response.dart'; // 引入上面的AddressComponent类
part 'place_model.g.dart';

@JsonSerializable(fieldRename: FieldRename.snake)
class Place {
  final String streetName;
  final String streetNumber;
  final String city;
  final String zipCode;

  Place({
    required this.streetName,
    required this.streetNumber,
    required this.city,
    required this.zipCode,
  });

  // 核心:从AddressComponent列表中提取对应字段,构建Place对象
  factory Place.fromAddressComponents(List<AddressComponent> components) {
    String streetName = '';
    String streetNumber = '';
    String city = '';
    String zipCode = '';

    // 遍历数组,根据types匹配对应的地址部分
    for (var component in components) {
      if (component.types.contains('route')) {
        streetName = component.shortName;
      } else if (component.types.contains('street_number')) {
        streetNumber = component.shortName;
      } else if (component.types.contains('locality')) {
        city = component.shortName;
      } else if (component.types.contains('postal_code')) {
        zipCode = component.shortName;
      }
    }

    // 可选:如果某个字段必填但没找到,可以在这里抛出错误或者设置默认值
    if (streetName.isEmpty || streetNumber.isEmpty) {
      throw Exception('无法提取必要的地址信息');
    }

    return Place(
      streetName: streetName,
      streetNumber: streetNumber,
      city: city,
      zipCode: zipCode,
    );
  }

  // 保留默认的fromJson(用于测试或其他场景)
  factory Place.fromJson(Map<String, dynamic> json) => _$PlaceFromJson(json);
}

3. 生成序列化代码

和之前一样,在终端运行以下命令,生成所有.g.dart文件:

flutter pub run build_runner build

这个命令会自动为所有标记了@JsonSerializable的类生成对应的序列化/反序列化代码,包括我们新创建的PlaceDetailsResponse、PlaceResult和AddressComponent。

4. 实际使用流程

当你从API拿到响应JSON后,按以下步骤获取Place对象:

// 假设apiResponse是你通过网络请求拿到的Map<String, dynamic>
final response = PlaceDetailsResponse.fromJson(apiResponse);
// 从响应中提取地址组件,构建Place
final place = Place.fromAddressComponents(response.result.addressComponents);

关键说明

  • 为什么之前的方法不行?因为默认的JsonSerializable只会直接映射JSON中的键值对,但你的Place模型里的字段在API返回的JSON中根本不存在——它们需要从数组元素中筛选提取,必须手动写逻辑处理。
  • 容错处理:如果API返回的address_components中缺少某个类型(比如某些地址没有locality),你可以根据业务需求调整fromAddressComponents方法,比如给字段设置默认值,或者抛出友好的错误提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 12:53:15