java.lang.ClassCastException:无法将java.util.LinkedHashMap强制转换为com.testing.models.Account


83

我遇到以下错误:

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)


无法投射到什么?错误消息的其余部分是什么?
奥利弗查尔斯沃思

1
@OliverCharlesworth还添加了整个stacktrace
热情的工程师,

2
什么Account啊 您为什么要尝试从地图上投射?
Dave

对于我们中可能不熟悉该库的那些人,您能说一下此given()方法是从哪个类静态导入的吗?
Mark Peters

@DaveNewtonAccount是Dropwizard的模型,使用的是com.fasterxml.jackson.databind.annotations
Passionate Engineer'3

Answers:


108

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

既然你不通知你的元素类型的杰克逊ArrayList,它不知道你要反序列化到ArrayListAccount秒。因此,它恢复为默认值。

相反,您可能会使用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);

您还可以将响应设置为asString(),省去了进行额外转换的麻烦-readValue将接受String作为第一个参数。
BIGDeutsch 2013年

@BIGDeutsch:再次回顾一下,我convertValue不需要在一步就能完成的情况下转换回令牌。也可以使用字符串。
Mark Peters

43

请尝试以下操作:

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

要么:

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

有关更多信息,请参见LinkedHashMap的转换


3
+1用于显示“ convertValue”。我有一种情况,我需要从json作为列表中读取特定属性,而这正是我所需要的。
GameSalutes

28

我可以减轻JSON数组来收集LinkedHashMap对象问题的方法是使用CollectionType而不是 TypeReference。这就是我所做的工作

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
也保存了我的问题。谢谢!
弗拉基米尔·吉列维奇(Fladimir Gilevich),

1
+1,您的情况不同是该方法本身是通用的,因此T无法通过TypeToken进行验证,这通常更容易。我个人更喜欢Guava的帮助程序,因为它仍然是类型安全的,并且不特定于任何容器类型:return new TypeToken<List<T>>(){}.where(new TypeParameter<T>(){}, tClass)。但是杰克逊不带TypeTokens,所以你就需要.getType()
Mark Peters

也保存了我的问题。谢谢!
沙申克

6

我有一个类似的异常(但问题不同)- 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));

1

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

  1. Whith类型是一个对象
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;
}

0

我有这种反序列化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 >>(){ });

0

当您使用杰克逊将字符串从映射到具体类时,尤其是在使用泛型类型时。那么可能由于不同的类加载器而发生此问题。我在下面的场景中遇到了一次:

项目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的类加载器中加载帐户类,而是仅重写convertJson项目B中的方法以让杰克逊完成其工作

public class ServiceAImpl extends ServiceA<Account> {
        @Override
        public DocSearchResponse<T> convertJson(String result){
         return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
      }
    }
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.