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
相关产品推荐
相关产品推荐

