如何将 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&lt;T&gt; type;List&lt;T&gt; myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(ArrayList.class, type)); 对于泛型类型: List 其中 T 是 MyObjectClass.... ''' List myObjects = mapper.readValue(json,mapper.getTypeFactory().constructCollectionType(ArrayList.class , MyObjectClass.class)); '''【参考方案2】:

进口:

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&lt;T&gt; 提取为 ApiResponse&lt;Object&gt; 并将其解析为 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 对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 dmesg 时间戳转换为自定义日期格式?

如何将自定义 json 转换为自适应卡片 json 格式

如何将 uitextview 内容转换为自定义大小的图像?

如何将 LRESULT 转换为自定义结构类型?

如何将时间字符串转换为自定义日期格式?

Datagridview如何将选定的行转换为自定义对象