java.lang.ClassCastException : java.util.LinkedHashMap을 com.testing.models.Account로 캐스트 할 수 없습니다.


84

아래 오류가 발생합니다.

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를 추가했습니다.
Passionate Engineer

2
무엇입니까 Account? 지도에서 캐스팅하려고하는 이유는 무엇입니까?
Dave Newton

라이브러리에 익숙하지 않은 분들을 위해이 given()메서드를 정적으로 가져온 클래스가 무엇인지 말씀해 주 시겠습니까?
Mark Peters

@DaveNewton Account은 Dropwizard 의 모델로com.fasterxml.jackson.databind.annotations
Passionate Engineer

답변:


108

문제는 잭슨에게서 나왔습니다. 역 직렬화 할 클래스에 대한 정보가 충분하지 않은 경우LinkedHashMap .

Jackson에의 요소 유형을 알리지 않았기 때문에 ArrayList으로 deserialize하려는 것을 알지 못합니다.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는 첫 번째 인수로 문자열을 허용합니다.
BIGDeutsch

@BIGDeutsch : 다시 한 번 살펴보면 convertValue한 번에 할 수 있을 때 토큰으로 다시 전환 할 필요가 없었 습니다. 문자열로 이동하는 것도 작동합니다.
Mark Peters

44

다음을 시도하십시오.

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

또는:

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

자세한 내용 은 LinkedHashMap 변환 을 참조하십시오.


3
"convertValue"표시에 +1. json에서 특정 속성을 List로 읽어야하는 경우가 있었는데 이것이 정확히 필요한 것입니다.
GameSalutes

28

LinkedHashMap 개체의 수집 문제에 대한 JSON 배열을 완화 할 수있는 방법 CollectionTypeTypeReference. 이것이 내가하고 일한 것입니다 .

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
내 문제도 구했습니다. 감사합니다!
Vladimir Gilevich

1
+1, 귀하의 경우 차이점은 메서드 자체가 일반적이므로 TTypeToken을 통해 수정할 수 없다는 것입니다. 일반적으로 더 쉽습니다. 개인적으로 저는 Guava의 도우미를 선호합니다. 왜냐하면 여전히 유형이 안전하고 어떤 컨테이너 유형에도 특정되지 않기 때문 return new TypeToken<List<T>>(){}.where(new TypeParameter<T>(){}, tClass)입니다. 그러나 Jackson은 TypeTokens를 사용 하지 않으므로 .getType().
Mark Peters

내 문제도 구했습니다. 감사합니다!
Shashank

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)

두 번째 줄에 오류가 발생하면 One 사용할 수 있습니다.

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

1

일반적인 두 가지 방법으로 문제 해결

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

0

XML을 deserialize하고 형식을 변환하는이 메서드가 있습니다.

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

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 클래스를로드 할 수없고 convertJsonjackson이 작업을 수행하도록 프로젝트 B의 메서드 를 재정의하기 때문에 발생합니다.

public class ServiceAImpl extends ServiceA<Account> {
        @Override
        public DocSearchResponse<T> convertJson(String result){
         return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
      }
    }
 }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.