Я получаю ошибку ниже:
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)
?
java
jackson
rest-assured
Страстный инженер
источник
источник
Account
? Почему вы пытаетесь выполнить трансляцию с карты?given()
статически импортируется этот метод?Account
- это модель от Dropwizard, которая используетcom.fasterxml.jackson.databind.annotations
Ответы:
Проблема исходит от Джексона. Когда у него недостаточно информации о том, в какой класс десериализоваться, он использует
LinkedHashMap
.Поскольку вы не сообщаете Джексону о типе вашего элемента
ArrayList
, он не знает, что вы хотите десериализовать в объектArrayList
изAccount
s. Таким образом, он возвращается к значению по умолчанию.Вместо этого вы, вероятно, могли бы использовать
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);
источник
convertValue
мог сделать это за один шаг. Переход к строке тоже работает.Попробуйте следующее:
или же:
List<POJO> pojos = mapper.convertValue( listOfObjects, new TypeReference<List<POJO>>() { });
См. Преобразование LinkedHashMap для получения дополнительной информации.
источник
Я мог смягчить проблему с массивом 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
, я все еще получал ArrayList из LinkedHashMaps, т.е. не работает :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; }
источник
T
нельзя было переопределить через TypeToken, что обычно проще. Лично я предпочитаю помощник гуавы, потому что она по - прежнему типизированная и не специфична для любого типа контейнера:return new TypeToken<List<T>>(){}.where(new TypeParameter<T>(){}, tClass)
. Но Джексон не принимаетTypeToken
так, что вам тогда нужно.getType()
.У меня было аналогичное исключение (но другая проблема) -
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));
источник
Решить проблему с помощью двух общих методов синтаксического анализа
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; }
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; }
источник
У меня есть этот метод десериализации 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 >>(){ });
источник
Когда вы используете Джексона для сопоставления строки с конкретным классом, особенно если вы работаете с универсальным типом. тогда эта проблема может возникнуть из-за другого загрузчика классов. Я встретил это однажды со сценарием ниже:
Проект B зависит от библиотеки A
в библиотеке A:
public class DocSearchResponse<T> { private T data; }
у него есть служба для запроса данных из внешнего источника и использования Джексона для преобразования в конкретный класс
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>>() {}); } }
в Проекте Б:
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, Джексон не может загрузить класс Account, а затем просто переопределить метод
convertJson
в Project B, чтобы позволить Джексону выполнять свою работуpublic class ServiceAImpl extends ServiceA<Account> { @Override public DocSearchResponse<T> convertJson(String result){ return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {}); } } }
источник