java.lang.ClassCastException: java.util.LinkedHashMap không thể được truyền tới com.testing.models.Account


84

Tôi gặp lỗi dưới đây:

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

với mã dưới đây

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

Có một lý do tại sao tôi không thể làm get(0)?


Không thể được chuyển thành những gì ? Phần còn lại của thông báo lỗi là gì?
Oliver Charlesworth

1
@OliverCharlesworth cũng đã thêm toàn bộ stacktrace
Kỹ sư đam mê

2
Một là Accountgì? Tại sao bạn lại cố truyền đến nó từ bản đồ?
Dave Newton

Đối với những người trong chúng ta có thể không quen với thư viện, bạn có thể cho biết given()phương thức này được nhập tĩnh từ lớp nào không?
Mark Peters

@DaveNewton Accountlà một mô hình từ Dropwizard trong đó sử dụngcom.fasterxml.jackson.databind.annotations
Kỹ sư Đam mê

Câu trả lời:


108

Vấn đề đến từ Jackson. Khi nó không có đủ thông tin về lớp nào cần giải mã, nó sẽ sử dụng LinkedHashMap.

Vì bạn không thông báo cho Jackson về loại phần tử của bạn ArrayList, nên nó không biết rằng bạn muốn deserialize thành một ArrayListtrong số Accounts. Vì vậy, nó rơi trở lại mặc định.

Thay vào đó, bạn có thể sử dụng as(JsonNode.class), và sau đó xử ObjectMapperlý theo cách phong phú hơn mức độ yên tâm cho phép. Một cái gì đó như thế này:

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

Bạn cũng có thể đặt phản hồi làString (), tiết kiệm việc phải thực hiện thêm các chuyển đổi - readValue sẽ chấp nhận một Chuỗi làm đối số đầu tiên.
BIGDeutsch

@BIGDeutsch: Chỉ cần xem lại điều này, tôi không cần phải chuyển đổi lại các mã thông báo ở đó khi convertValuecó thể làm điều đó trong một bước. Đi đến một chuỗi cũng hoạt động.
Mark Peters

44

Hãy thử những cách sau:

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

hoặc là:

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

Xem chuyển đổi của LinkedHashMap để biết thêm thông tin.


3
+1 để hiển thị "convertValue". Tôi đã gặp một trường hợp mà tôi cần đọc một thuộc tính cụ thể từ json dưới dạng Danh sách và đây chính xác là những gì tôi cần.
GameSalutes

28

Cách tôi có thể giảm thiểu sự cố Mảng JSON để thu thập các đối tượng LinkedHashMap là sử dụng CollectionTypethay vì sử dụng a TypeReference. Đây là những gì tôi đã làm và làm việc :

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

Sử dụng TypeReference, tôi vẫn nhận được ArrayList of LinkedHashMaps, tức là không hoạt động :

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
Đã cứu sự cố của tôi. Cảm ơn bạn!
Vladimir Gilevich

1
+1, sự khác biệt trong trường hợp của bạn là bản thân phương pháp này là chung chung nên Tkhông thể sửa đổi thông qua TypeToken, điều này thường dễ dàng hơn. Cá nhân tôi thích helper ổi vì nó vẫn còn typesafe và không cụ thể cho bất kỳ loại container: return new TypeToken<List<T>>(){}.where(new TypeParameter<T>(){}, tClass). Nhưng Jackson không lấy TypeTokennên bạn cần .getType().
Mark Peters

Đã cứu sự cố của tôi. Cảm ơn bạn!
Shashank

6

Tôi đã gặp một ngoại lệ tương tự (nhưng vấn đề khác) - java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to org.bson.Documentvà may mắn thay, nó được giải quyết dễ dàng hơn:

Thay vì

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

điều này gây ra lỗi trên dòng thứ hai, Người ta có thể sử dụng

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

1

Giải quyết vấn đề với hai phương pháp phân tích cú pháp phổ biến

  1. Loại Whith là một đối tượng
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. Với loại là đối tượng bọc bộ sưu tập
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

Tôi có phương pháp này để giải mã XML và chuyển đổi kiểu:

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

và đây là cuộc gọi:

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

0

Khi bạn sử dụng jackson để ánh xạ từ chuỗi sang lớp cụ thể của mình, đặc biệt nếu bạn làm việc với kiểu chung. thì sự cố này có thể xảy ra do trình tải lớp khác nhau. tôi đã gặp nó một lần với kịch bản dưới đây:

Dự án B phụ thuộc vào Thư viện A

trong Thư viện A:

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

nó có dịch vụ truy vấn dữ liệu từ nguồn bên ngoài và sử dụng jackson để chuyển đổi sang lớp cụ thể

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

trong Dự án B:

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

và tôi sử dụng ServiceA từ thư viện để thực hiện truy vấn và cũng như chuyển đổi dữ liệu

public class ServiceAImpl extends ServiceA<Account> {
    
}

và tận dụng nó

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
    }
}

nó xảy ra bởi vì từ trình tải lớp của LibraryA, jackson không thể tải lớp Tài khoản, khi đó chỉ cần ghi đè phương thức convertJsontrong Dự án B để jackson thực hiện công việc của nó

public class ServiceAImpl extends ServiceA<Account> {
        @Override
        public DocSearchResponse<T> convertJson(String result){
         return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
      }
    }
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.