如何用Stream/Jackson将Google Places API地址组件填充到Java实体类
问题描述
我拿到了Google Places API返回的address_components数据如下:
"address_components": [ { "long_name": "123", "short_name": "123", "types": ["street_number"] }, { "long_name": "Mulgoa Road", "short_name": "Mulgoa Rd", "types": ["route"] }, { "long_name": "Penrith", "short_name": "Penrith", "types": ["locality", "political"] }, { "long_name": "City of Penrith", "short_name": "City of Penrith", "types": ["administrative_area_level_2", "political"] }, { "long_name": "New South Wales", "short_name": "NSW", "types": ["administrative_area_level_1", "political"] }, { "long_name": "Australia", "short_name": "AU", "types": ["country", "political"] }, { "long_name": "2750", "short_name": "2750", "types": ["postal_code"] } ]
期望转换成如下格式的响应:
{ "postcode": "2750", "unit_number": null, "floor_number": null, "building_name": null, "building_number": null, "block_number": null, "street_number": "123", "street_name": "Mulgoa", "street_type": "Road", "state": "New South Wales", "suburb": "Penrith", "city": "City of Penrith", "district": null, "fullAddress": "123 Mulgoa Road, Penrith NSW 2750, Australia" }
当前已有部分实现代码:
JsonNode resultData = new ObjectMapper().readTree(String.valueOf(response.getResponseBody())); if (Objects.equals(resultData.get("status").asText(), "OK")) { JsonNode addressComponentsArray = resultData.get("result").get("address_components"); LocationAddressComponent responseObject = new LocationAddressComponent(); if (addressComponentsArray.isArray()) { for (JsonNode jsonNode : addressComponentsArray) { Array res = jsonNode.get("types"); // 待填充逻辑 } } }
实体类LocationAddressComponent.java定义:
public class LocationAddressComponent { String postcode; String unit_number; String floor_number; String building_name; String building_number; String block_number; String street_number; String street_name; String street_type; String state; String suburb; String city; String district; String fullAddress; // 需添加getter、setter,或用Lombok注解简化 }
需要给出基于Stream或Jackson完成数据填充的具体方案。
方案一:Stream遍历映射 + 手动赋值
利用Java Stream遍历地址组件数组,通过类型匹配完成字段赋值,最后拼接完整地址。
JsonNode resultData = new ObjectMapper().readTree(String.valueOf(response.getResponseBody())); if (Objects.equals(resultData.get("status").asText(), "OK")) { JsonNode addressComponentsArray = resultData.get("result").get("address_components"); LocationAddressComponent responseObject = new LocationAddressComponent(); if (addressComponentsArray.isArray()) { // 将JsonNode数组转为Stream处理 StreamSupport.stream(addressComponentsArray.spliterator(), false) .forEach(component -> { List<String> types = component.get("types") .findValuesAsText() .stream() .collect(Collectors.toList()); String longName = component.get("long_name").asText(); String shortName = component.get("short_name").asText(); // 按类型匹配赋值 if (types.contains("street_number")) { responseObject.setStreet_number(longName); } else if (types.contains("route")) { // 拆分街道名与类型(如Mulgoa Road拆为Mulgoa和Road) String[] streetParts = longName.split(" ", 2); if (streetParts.length == 2) { responseObject.setStreet_name(streetParts[0]); responseObject.setStreet_type(streetParts[1]); } else { responseObject.setStreet_name(longName); } } else if (types.contains("locality")) { responseObject.setSuburb(longName); } else if (types.contains("administrative_area_level_2")) { responseObject.setCity(longName); } else if (types.contains("administrative_area_level_1")) { responseObject.setState(longName); } else if (types.contains("postal_code")) { responseObject.setPostcode(longName); } }); // 拼接完整地址 StringBuilder fullAddr = new StringBuilder(); Optional.ofNullable(responseObject.getStreet_number()).ifPresent(s -> fullAddr.append(s).append(" ")); if (responseObject.getStreet_name() != null && responseObject.getStreet_type() != null) { fullAddr.append(responseObject.getStreet_name()).append(" ").append(responseObject.getStreet_type()); } else { Optional.ofNullable(responseObject.getStreet_name()).ifPresent(fullAddr::append); } Optional.ofNullable(responseObject.getSuburb()).ifPresent(s -> fullAddr.append(", ").append(s)); // 补充州缩写和邮编 JsonNode stateComponent = StreamSupport.stream(addressComponentsArray.spliterator(), false) .filter(n -> n.get("types").findValuesAsText().contains("administrative_area_level_1")) .findFirst().orElse(null); if (stateComponent != null && responseObject.getPostcode() != null) { fullAddr.append(" ").append(stateComponent.get("short_name").asText()).append(" ").append(responseObject.getPostcode()); } fullAddr.append(", Australia"); responseObject.setFullAddress(fullAddr.toString().trim()); } }
注意:
- 需给
LocationAddressComponent添加所有字段的getter/setter,或使用Lombok的@Data注解自动生成。 - 街道名拆分逻辑需根据实际地区格式调整,部分场景可能需要更灵活的处理。
方案二:Jackson自定义反序列化器
通过自定义Jackson反序列化器,直接将API响应映射到目标实体类,代码更符合序列化规范且复用性强。
步骤1:编写自定义反序列化器
public class LocationAddressDeserializer extends StdDeserializer<LocationAddressComponent> { public LocationAddressDeserializer() { this(null); } public LocationAddressDeserializer(Class<?> vc) { super(vc); } @Override public LocationAddressComponent deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode root = p.getCodec().readTree(p); JsonNode addressComponents = root.get("result").get("address_components"); LocationAddressComponent addr = new LocationAddressComponent(); String stateShort = null; // 遍历赋值 for (JsonNode component : addressComponents) { List<String> types = component.get("types").findValuesAsText(); String longName = component.get("long_name").asText(); String shortName = component.get("short_name").asText(); if (types.contains("street_number")) { addr.setStreet_number(longName); } else if (types.contains("route")) { String[] streetParts = longName.split(" ", 2); if (streetParts.length == 2) { addr.setStreet_name(streetParts[0]); addr.setStreet_type(streetParts[1]); } else { addr.setStreet_name(longName); } } else if (types.contains("locality")) { addr.setSuburb(longName); } else if (types.contains("administrative_area_level_2")) { addr.setCity(longName); } else if (types.contains("administrative_area_level_1")) { addr.setState(longName); stateShort = shortName; } else if (types.contains("postal_code")) { addr.setPostcode(longName); } } // 拼接完整地址 StringBuilder fullAddr = new StringBuilder(); Optional.ofNullable(addr.getStreet_number()).ifPresent(s -> fullAddr.append(s).append(" ")); if (addr.getStreet_name() != null && addr.getStreet_type() != null) { fullAddr.append(addr.getStreet_name()).append(" ").append(addr.getStreet_type()); } else { Optional.ofNullable(addr.getStreet_name()).ifPresent(fullAddr::append); } Optional.ofNullable(addr.getSuburb()).ifPresent(s -> fullAddr.append(", ").append(s)); if (stateShort != null && addr.getPostcode() != null) { fullAddr.append(" ").append(stateShort).append(" ").append(addr.getPostcode()); } fullAddr.append(", Australia"); addr.setFullAddress(fullAddr.toString().trim()); return addr; } }
步骤2:给实体类添加反序列化注解
@Data // Lombok注解,自动生成getter、setter等方法 @JsonDeserialize(using = LocationAddressDeserializer.class) public class LocationAddressComponent { String postcode; String unit_number; String floor_number; String building_name; String building_number; String block_number; String street_number; String street_name; String street_type; String state; String suburb; String city; String district; String fullAddress; }
步骤3:直接反序列化API响应
ObjectMapper mapper = new ObjectMapper(); // 注册Lombok模块,确保Jackson能识别Lombok生成的方法(需引入jackson-datatype-lombok依赖) mapper.registerModule(new LombokModule()); try { LocationAddressComponent responseObject = mapper.readValue(response.getResponseBody(), LocationAddressComponent.class); // 使用responseObject } catch (IOException e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者xMayank
相关产品推荐
相关产品推荐

