【问题标题】:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.testing.models.Accountjava.lang.ClassCastException:java.util.LinkedHashMap 无法转换为 com.testing.models.Account
【发布时间】:2015-05-03 12:12:34
【问题描述】:

我遇到以下错误:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.testing.models.Account

下面的代码

final int expectedId = 1;

Test newTest = create();

int expectedResponseCode = Response.SC_OK;

ArrayList<Account> account = given().when().expect().statusCode(expectedResponseCode)
    .get("accounts/" + newTest.id() + "/users")
    .as(ArrayList.class);
assertThat(account.get(0).getId()).isEqualTo(expectedId);

我不能做get(0)有什么原因吗?

【问题讨论】:

  • 不能转换成什么?错误信息的其余部分是什么?
  • @OliverCharlesworth 还添加了整个堆栈跟踪
  • 什么是Account?你为什么要从地图投射到它?
  • 对于我们这些可能对库不熟悉的人,您能说一下这个given()方法是从哪个类静态导入的吗?
  • @DaveNewton Account 是 Dropwizard 的一个模型,它使用 com.fasterxml.jackson.databind.annotations

标签: java jackson rest-assured


【解决方案1】:

问题来自杰克逊。当它没有足够的信息来反序列化到哪个类时,它使用LinkedHashMap

由于您没有将ArrayList 的元素类型通知Jackson,因此它不知道您要反序列化为ArrayListAccounts。所以它会回退到默认值。

相反,您可以使用as(JsonNode.class),然后以比放心允许的更丰富的方式处理ObjectMapper。像这样的:

ObjectMapper mapper = new ObjectMapper();

JsonNode accounts = given().when().expect().statusCode(expectedResponseCode)
    .get("accounts/" + newClub.getOwner().getCustId() + "/clubs")
    .as(JsonNode.class);


//Jackson's use of generics here are completely unsafe, but that's another issue
List<Account> accountList = mapper.convertValue(
    accounts, 
    new TypeReference<List<Account>>(){}
);

assertThat(accountList.get(0).getId()).isEqualTo(expectedId);

【讨论】:

  • 您也可以将响应设置为String(),省去额外的转换——readValue 将接受一个字符串作为第一个参数。
  • @BIGDeutsch:只是重温一下,当convertValue 可以一步完成时,我不需要在那里转换回令牌。转到字符串也可以。
  • 当一个对象包含一个变量列表时该怎么做。如何使用jackson反序列化对象?
【解决方案2】:

尝试以下方法:

POJO pojo = mapper.convertValue(singleObject, POJO.class);

或:

List<POJO> pojos = mapper.convertValue(
    listOfObjects,
    new TypeReference<List<POJO>>() { });

更多信息请参见conversion of LinkedHashMap

【讨论】:

  • +1 用于显示“convertValue”。我有一个案例,我需要从 json 中读取特定属性作为列表,这正是我所需要的。
【解决方案3】:

我可以通过使用 CollectionType 而不是 TypeReference 来缓解 JSON 数组到 LinkedHashMap 对象集合的问题。 这就是我所做的并且工作

public <T> List<T> jsonArrayToObjectList(String json, Class<T> tClass) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    CollectionType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, tClass);
    List<T> ts = mapper.readValue(json, listType);
    LOGGER.debug("class name: {}", ts.get(0).getClass().getName());
    return ts;
}

使用TypeReference,我仍然得到一个LinkedHashMaps的ArrayList,即不起作用

public <T> List<T> jsonArrayToObjectList(String json, Class<T> tClass) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    List<T> ts = mapper.readValue(json, new TypeReference<List<T>>(){});
    LOGGER.debug("class name: {}", ts.get(0).getClass().getName());
    return ts;
}

【讨论】:

  • 也保存了我的问题。谢谢!
  • +1,您的情况的不同之处在于该方法本身是通用的,因此 T 无法通过 TypeToken 进行具体化,这通常更容易。我个人更喜欢 Guava 的助手,因为它仍然是类型安全的,并且不特定于任何容器类型:return new TypeToken&lt;List&lt;T&gt;&gt;(){}.where(new TypeParameter&lt;T&gt;(){}, tClass)。但是杰克逊不接受TypeTokens,所以你需要.getType()
  • 也保存了我的问题。谢谢!
  • 谢谢!花了很多时间来修复这个错误,直到我找到这个答案!
  • 可爱!!。这也节省了我的一天:)..在我找到这个解决方案之前挣扎了几个小时。我试图在 kotlin 中为 JOOQ 编写一个通用 JSONB 数组转换器。
【解决方案4】:

我有一个类似的例外(但不同的问题) - java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to org.bson.Document ,幸运的是它更容易解决:

代替

List<Document> docs = obj.get("documents");
Document doc = docs.get(0)

在第二行给出错误,可以使用

List<Document> docs = obj.get("documents");
Document doc = new Document(docs.get(0));

【讨论】:

  • 您是如何为 Document 类设置构造函数的?
【解决方案5】:

用两种方法解析共同解决问题

  1. 类型是对象
public <T> T jsonToObject(String json, Class<T> type) {
        T target = null;
        try {
            target = objectMapper.readValue(json, type);
        } catch (Jsenter code hereonProcessingException e) {
            e.printStackTrace();
        }
    
        return target;
    }
  1. 类型是集合包装对象
public <T> T jsonToObject(String json, TypeReference<T> type) {
    T target = null;
    try {
        target = objectMapper.readValue(json, type);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    return target;
}

【讨论】:

    【解决方案6】:

    这是我在我的项目中使用的东西,返回了 Json 对象,我将其转换为 POJO 的列表,列表然后访问该元素。 我从另一个微服务中获取了 Json 对象的输入。

    主要是:- JsonNode 股票 = restTemplate.getForObject("http://localhost:2000/stocks/qty", JsonNode.class); List stockList = mapper.convertValue(stocks, new TypeReference>() {});

    @GetMapping("/")
        public List<Stock_id_qty> checkQty() throws JsonProcessingException {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode stocks = restTemplate.getForObject("http://localhost:2000/stocks/qty", JsonNode.class);
            List<Stock_id_qty> stockList = mapper.convertValue(stocks, new TypeReference<List<Stock_id_qty>>() {});
            List<Stock_id_qty> result = new ArrayList<>();
            for(Stock_id_qty s : stockList){
                if(s.getStockQty() < 10)
                {
                    result.add(s);
                }
            }
            return result;
        }
    

    【讨论】:

      【解决方案7】:

      我有这种反序列化 XML 和转换类型的方法:

      public <T> Object deserialize(String xml, Class objClass ,TypeReference<T> typeReference ) throws IOException {
          XmlMapper xmlMapper = new XmlMapper();
          Object obj = xmlMapper.readValue(xml,objClass);
          return  xmlMapper.convertValue(obj,typeReference );   
      }
      

      这是电话:

      List<POJO> pojos = (List<POJO>) MyUtilClass.deserialize(xml, ArrayList.class,new TypeReference< List< POJO >>(){ });
      

      【讨论】:

        【解决方案8】:

        当您使用 jackson 从字符串映射到具体类时,尤其是在使用泛型类型时。那么这个问题可能是由于不同的类加载器而发生的。我曾在以下场景中遇到过它:

        项目 B 依赖于库 A

        在图书馆 A:

        public class DocSearchResponse<T> {
         private T data;
        }
        

        它具有从外部查询数据的服务,并使用jackson转换为具体类

        public class ServiceA<T>{
          @Autowired
          private ObjectMapper mapper;
          @Autowired
          private ClientDocSearch searchClient;
        
          public DocSearchResponse<T> query(Criteria criteria){
              String resultInString = searchClient.search(criteria);
              return convertJson(resultInString)
          }
        }
        
        public DocSearchResponse<T> convertJson(String result){
             return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
          }
        }
        

        在项目 B 中:

        public class Account{
         private String name;
         //come with other attributes
        }
        

        我使用库中的 ServiceA 进行查询并转换数据

        public class ServiceAImpl extends ServiceA<Account> {
            
        }
        

        并利用它

        public class MakingAccountService {
            @Autowired
            private ServiceA service;
            public void execute(Criteria criteria){
              
                DocSearchResponse<Account> result = service.query(criteria);
                Account acc = result.getData(); //  java.util.LinkedHashMap cannot be cast to com.testing.models.Account
            }
        }
        

        这是因为从 LibraryA 的类加载器中,jackson 无法加载 Account 类,然后只需覆盖项目 B 中的方法 convertJson 让 jackson 完成它的工作

        public class ServiceAImpl extends ServiceA<Account> {
                @Override
                public DocSearchResponse<T> convertJson(String result){
                 return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
              }
            }
         }
        

        【讨论】:

          【解决方案9】:
          public class ObjectHelper {
          
            private static ObjectMapper objectMapper = new ObjectMapper();
          
            public static ObjectMapper getObjectMapper() {
              objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
              return objectMapper;
            } 
          }
          

          使用

          FetchResponse fetchResponse =
          ObjectHelper.getObjectMapper().convertValue(
                              data, new TypeReference<FetchResponse>() {});
          

          List<Map<String, Object>> responseObj = (List<Map<String, Object>>) response.get("content");
          
          List<LkAuthUserDetail> responseData = ObjectHelper.getObjectMapper().convertValue(responseObj,
                              new TypeReference<List<LkAuthUserDetail>>() {});
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-02-02
            • 2016-06-30
            • 2016-01-14
            相关资源
            最近更新 更多