使用Jackson ObjectMapper将JSON映射至Google DirectionsResult时,EncodedPolyline的points属性无法赋值的解决方法
解决Jackson映射Google DirectionsResult时overview_polyline.points为null的问题
这个问题的核心在于Google Maps Java客户端库中的EncodedPolyline类的设计限制:它的points字段是私有且没有公共setter方法,同时无参构造器直接将points初始化为null。Jackson默认会通过无参构造器创建对象,然后尝试通过setter或直接访问字段来赋值,但由于上述限制,无法将JSON中的points值注入进去。
下面提供几种可行的解决方案,按推荐程度排序:
方案一:自定义EncodedPolyline反序列化器(推荐)
我们可以为EncodedPolyline编写一个自定义的Jackson反序列化器,通过反射绕过私有字段的访问限制,直接设置points值:
1. 实现反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.google.maps.model.EncodedPolyline; import java.io.IOException; import java.lang.reflect.Field; public class EncodedPolylineDeserializer extends StdDeserializer<EncodedPolyline> { public EncodedPolylineDeserializer() { super(EncodedPolyline.class); } @Override public EncodedPolyline deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 读取JSON节点中的points值 JsonNode node = p.getCodec().readTree(p); String points = node.get("points").asText(); // 创建EncodedPolyline实例并通过反射设置points字段 EncodedPolyline polyline = new EncodedPolyline(); try { Field pointsField = EncodedPolyline.class.getDeclaredField("points"); pointsField.setAccessible(true); // 允许访问私有字段 pointsField.set(polyline, points); } catch (NoSuchFieldException | IllegalAccessException e) { throw new IOException("Failed to inject points into EncodedPolyline", e); } return polyline; } }
2. 注册反序列化器到ObjectMapper
在你的映射代码中,把这个反序列化器注册到ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 注册自定义反序列化器 objectMapper.registerModule(new SimpleModule() .addDeserializer(EncodedPolyline.class, new EncodedPolylineDeserializer())); // 现在再进行映射 DirectionsResult result = objectMapper.readValue(json, DirectionsResult.class);
方案二:使用适配POJO中转
如果不想编写反序列化器,可以先将JSON映射到自己定义的适配POJO,再手动转换为Google的DirectionsResult对象:
1. 定义适配POJO
import com.google.maps.model.Bounds; import com.google.maps.model.Leg; import com.google.maps.model.GeocodedWaypoint; import java.util.List; public class MyDirectionsResult { private List<GeocodedWaypoint> geocodedWaypoints; private List<MyRoute> routes; // Getters and Setters public static class MyRoute { private Bounds bounds; private List<Leg> legs; private MyEncodedPolyline overviewPolyline; private List<String> warnings; private List<Integer> waypointOrder; // Getters and Setters } public static class MyEncodedPolyline { private String points; // Getters and Setters } }
2. 转换为Google的DirectionsResult
// 先映射到自定义POJO MyDirectionsResult myResult = objectMapper.readValue(json, MyDirectionsResult.class); // 手动转换为Google的DirectionsResult DirectionsResult result = new DirectionsResult(); result.geocodedWaypoints = myResult.getGeocodedWaypoints().toArray(new GeocodedWaypoint[0]); List<com.google.maps.model.Route> googleRoutes = new ArrayList<>(); for (MyRoute myRoute : myResult.getRoutes()) { com.google.maps.model.Route googleRoute = new com.google.maps.model.Route(); googleRoute.bounds = myRoute.getBounds(); googleRoute.legs = myRoute.getLegs().toArray(new Leg[0]); googleRoute.warnings = myRoute.getWarnings().toArray(new String[0]); googleRoute.waypointOrder = myRoute.getWaypointOrder().stream().mapToInt(Integer::intValue).toArray(); // 处理overviewPolyline EncodedPolyline polyline = new EncodedPolyline(); try { Field pointsField = EncodedPolyline.class.getDeclaredField("points"); pointsField.setAccessible(true); pointsField.set(polyline, myRoute.getOverviewPolyline().getPoints()); googleRoute.overviewPolyline = polyline; } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } googleRoutes.add(googleRoute); } result.routes = googleRoutes.toArray(new com.google.maps.model.Route[0]);
方案三:直接反射修复(应急用)
如果只是临时解决,可以在映射完成后,通过反射遍历routes中的overviewPolyline,手动设置points值:
DirectionsResult result = objectMapper.readValue(json, DirectionsResult.class); // 遍历所有route,修复overviewPolyline的points for (com.google.maps.model.Route route : result.routes) { if (route.overviewPolyline != null) { // 从JSON中重新提取points(或者你可以在映射时先把JSON存下来) JsonNode rootNode = objectMapper.readTree(json); String points = rootNode.get("routes").get(0).get("overview_polyline").get("points").asText(); try { Field pointsField = EncodedPolyline.class.getDeclaredField("points"); pointsField.setAccessible(true); pointsField.set(route.overviewPolyline, points); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } }
这个方案比较繁琐,不推荐在生产环境使用,但应急时可以快速生效。
内容的提问来源于stack exchange,提问作者phạm Tiến
相关产品推荐
相关产品推荐

