如何将 LinkedHashMap 转换为自定义 java 对象?
Posted
技术标签:
【中文标题】如何将 LinkedHashMap 转换为自定义 java 对象?【英文标题】:how to convert LinkedHashMap to Custom java object? 【发布时间】:2014-04-17 00:30:55 【问题描述】:我正在尝试通过 RESTful WS 将数据从一个应用程序获取到另一个应用程序,它可以工作,但我无法使用这些数据,因为我无法转换它... WS 返回一个对象列表,如下所示:
id=1, forename=John, surname=Bloggs, username=jbloggs, role=Graduate Developer, office=London, skills=[technology=Java, experience=2.5, technology=Web, experience=2.0, technology=ios, experience=0.0, technology=.NET, experience=0.0]
我使用 Jackson 的 ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
List<ConsultantDto> list = new ArrayList<ConsultantDto>();
try
list = mapper.readValue(con.getInputStream(), ArrayList.class);
catch (JsonGenerationException e)
e.printStackTrace();
catch (JsonMappingException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
之后我有 3 行代码:
System.out.println(list.get(0));
System.out.println(list.get(0).getForename());
return list;
return 因为这个方法的返回值被传递给在浏览器中显示正确数据的其他 web 服务。有趣的事情发生在两条打印行上,一条打印这篇文章顶部的数据 (id:1 ... ),但另一条抛出异常:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.xxx.xxx.web.dto.rp.ConsultantDto
ConsultantDto 和 SkillDto 是两个合法的类,它们的所有属性都设置为匹配来自 WS 的数据,所有 getter/setter 都已就位。就我而言,LinkedHashMap 将内容存储为键:值对,所以我只是看不出这个异常来自哪里。我该如何解决它,为什么 ObjectMapper 不能正确解析值(当我得到一个 ConsultantDto 而不是 List 时它会这样做)?
【问题讨论】:
【参考方案1】:你需要这样做:
List<ConsultantDto> myObjects =
mapper.readValue(jsonInput, new TypeReference<List<ConsultantDto>>());
(来自SO answer)
您必须使用TypeReference
的原因是Java 的一个不幸的怪癖。如果 Java 有适当的泛型,我敢打赌你的语法会奏效。
【讨论】:
@Enno 使用泛型 T 类型可以实现同样的事情吗? 使用泛型类型 T 使用:Class<T> type;
List<T> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(ArrayList.class, type));
对于泛型类型: List进口:
import com.fasterxml.jackson.databind.ObjectMapper;
对象:
private ObjectMapper mapper = new ObjectMapper();
例子:
PremierDriverInfoVariationDTO premierDriverInfoDTO =
mapper.convertValue(json, PremierDriverInfoVariationDTO.class);
log.debug("premierDriverInfoDTO : ", premierDriverInfoDTO);
或
Map<String, Boolean> map = mapper.convertValue(json, Map.class);
log.debug("result : ", map);
assertFalse(map.get("eligiblePDJob"));
【讨论】:
【参考方案3】:在我的用例中,我有 10 个不同的 API。我所有的 API 响应都采用相同的 json 格式,但其中一个标签的内容不同 - /result/data
"success": true,
"result":
"type": "transactions",
"data":
,
"error": [],
"metadata":
"version": "v1",
"code": 200,
"desc": "OK",
"trackingId": "TRACKINGID-1588097123800-1234567890",
"generatedTimestamp": "2020-07-14T09:41:06.198Z"
,
"link":
"self": "/v1/myapp/customer/enquiry"
在我的 Spring Boot 代码中,我对所有 API 调用都使用了以下通用方法 -
<T, R> ApiResponse<R> execute(URI uri, T entity, Class<R> clazz)
HttpEntity<T> httpEntity = new HttpEntity<>(entity);
ApiResponse<R> body = this.restTemplate.exchange(uri, HttpMethod.POST, httpEntity,
new ParameterizedTypeReference<ApiResponse<R>>()
).getBody();
return body;
它给了我与您上面报告的相同的错误。基于this和其他一些SO答案,我也尝试了以下方法,但没有成功-
<T, R> ApiResponse<R> execute(URI uri, T entity, Class<R> clazz)
HttpEntity<T> httpEntity = new HttpEntity<>(entity);
ResponseEntity<String> response = this.scVaultCoreApi.exchange(uri, HttpMethod.POST, httpEntity, String.class);
ObjectMapper mapper = new ObjectMapper();
try
return mapper.readValue(response.getBody(), new TypeReference<ApiResponse<R>>()
);
catch (Exception e)
throw new RuntimeException();
另一个可行的解决方案是使用 jackson 中的 ObjectMapper 手动解析响应流。但是对于我正在使用的这么多 API,我无法做到这一点,并且上述错误仅出现在某些 API 调用中。因此,仅对于那些 API,而不是依赖于 TypeReference 到 Class 的转换,我没有更改我定义的上述方法,而是将 ApiResponse<T>
提取为 ApiResponse<Object>
并将其解析为 LinkedHashMap
only 和手动创建了我的特定类对象。
ApiResult<MyResponse> res = execute(uri, payload, MyResponse.class).getResult();
Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) res.getData();
MyResponse myResponse = MyResponse.builder()
.accountBalance(new BigDecimal(map.get("key").get("balance").toString()))
.clientName((String) map.get("key").get("clientName"))
.build();
这个解决方案的最大优点是我不必更改基类 execute
方法,并且其他 API 调用工作正常,并且仅针对有问题的 API,我编写了手动对象创建代码。
【讨论】:
以上是关于如何将 LinkedHashMap 转换为自定义 java 对象?的主要内容,如果未能解决你的问题,请参考以下文章