如何将LinkedHashMap转换为Custom java对象?

时间:2014-03-12 17:09:19

标签: java json web-services

我正在尝试通过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;

返回,因为此方法的返回值被传递给在浏览器中显示正确数据的其他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时它会这样做?)

3 个答案:

答案 0 :(得分:38)

你需要这样做:

List<ConsultantDto> myObjects =
    mapper.readValue(jsonInput, new TypeReference<List<ConsultantDto>>(){});

(来自此SO answer

你必须使用TypeReference的原因是因为Java的一个不幸的怪癖。如果Java有一个合适的泛型,我敢打赌你的语法会起作用。

答案 1 :(得分:0)

导入:

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"));

答案 2 :(得分:0)

在我的用例中,我有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"
    }
}

在我的春季启动代码中,我对所有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;
}

它给了我与您上面报告的相同的错误。基于此答案以及其他一些答案,我也尝试了以下方法,但它不起作用-

<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();
    }
}

另一个可行的解决方案是使用杰克逊中的ObjectMapper手动解析响应流。但是对于我使用的这么多API,我无法做到这一点,并且上述错误仅在某些API调用中出现。因此,仅针对那些API,我没有更改我定义的上述方法,而是依靠将ApiResponse<T>提取为ApiResponse<Object>并将其解析为{仅{1}},并手动创建了我的特定类对象。

LinkedHashMap

该解决方案的最好之处在于,我不必更改基类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(); 的方法,其他API调用也可以正常工作,并且仅针对有问题的API,我编写了手动对象创建代码。