шаблон остальных тестов: не удалось извлечь ответ: не найден подходящий HttpMessageConverter для типа ответа - PullRequest
1 голос
/ 12 октября 2019

Я получаю это исключение

org.springframework.web.client.RestClientException: Не удалось извлечь ответ: не найден подходящий HttpMessageConverter для типа ответа [AnalyticsResponse] и типа содержимого [application / json; charset= UTF-8]

только в моих тестах junit ( Остальные конечные точки работают нормально ), если мой DTO содержит карту с определенным пользователем классом в качестве ключа

@Data
public class AnalyticsResponse {

    private List<Committer> commitersList; //OK
    private Map<Committer , Long> comittersCommitsMap; // Problem
    private Map<Date, List<CommitItem>> commitItemsTimeLineMap; //OK

}

Если поле comittersCommitsMap удалено, все идет хорошо

мой фрагмент кода теста:

ResponseEntity<AnalyticsResponse> analyticsResponse = testRestTemplate.getForEntity(ANALYSIS_CONTROLLER_BASE_URL+"analytics?repo-full-name=" + searchResponse.getBody().get(0).getFull_name() ,
                AnalyticsResponse.class); 

--- update: класс Committer

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Committer {

    private  String name;
    private String email;

}

1 Ответ

0 голосов
/ 13 октября 2019

По умолчанию ObjectMapper не может определить сериализацию и десериализацию для Map, в котором key не является String, вы должны предоставить свою пользовательскую реализацию.

Реализация KeyDeserializer и JsonSerializer для Committer и Date (в качестве ключа для Карта )

Настройка ObjectMapper - зарегистрируйте модуль с KeyDeserializer и KeySerializer ( JsonSerializer )

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    // KeyDeserializer for Committer (simple without 'null' check)
    @RequiredArgsConstructor
    public static class CommitterKeyDeserializer extends KeyDeserializer {

        private final ObjectMapper mapper;

        @Override
        public Object deserializeKey(final String key,
                                     final DeserializationContext ctxt)
                throws IOException, JsonProcessingException {

            return mapper.readValue(key, Committer.class);
        }
    }

    // KeyDeserializer for Date (simple without 'null' check)
    @RequiredArgsConstructor
    public static class DateKeyDeserializer extends KeyDeserializer {

        private final ObjectMapper mapper;

        @Override
        public Object deserializeKey(final String key,
                                     final DeserializationContext ctxt)
                throws IOException, JsonProcessingException {

            return mapper.readValue(key, Date.class);
        }
    }

    // JsonSerializer for Committer (simple without 'null' check)
    @RequiredArgsConstructor
    public static class CommitterJsonSerializer extends JsonSerializer<Committer> {

        private final ObjectMapper mapper;

        @Override
        public void serialize(Committer committer,
                              JsonGenerator jgen,
                              SerializerProvider provider)
                throws IOException, JsonProcessingException {

            jgen.writeFieldName(mapper.writeValueAsString(committer));
        }

    }

    // JsonSerializer for Date (simple without 'null' check)
    public static class DateJsonSerializer extends JsonSerializer<Date> {

        @Override
        public void serialize(Date date,
                              JsonGenerator jgen,
                              SerializerProvider provider)
                throws IOException, JsonProcessingException {

            jgen.writeFieldName(String.valueOf(date.getTime()));
        }

    }

    // ObjectMapper configuration
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        // register module with custom serializers and deserializers
        mapper.registerModule(new SimpleModule()
                .addKeyDeserializer(
                        Committer.class,
                        new CommitterKeyDeserializer(mapper))
                .addKeyDeserializer(
                        Date.class,
                        new DateKeyDeserializer(mapper))
                .addKeySerializer(
                        Committer.class,
                        new CommitterJsonSerializer(mapper))
                .addKeySerializer(
                        Date.class,
                        new DateJsonSerializer()));

        return mapper;
    }

    // RestTemplate configuration
    @Bean
    public RestTemplate restTemplate(List<HttpMessageConverter<?>> converters) {
        RestTemplate restTemplate = new RestTemplate();
        // add spring's predefined converters
        restTemplate.setMessageConverters(converters);

        return restTemplate;
    }

}

Обратите внимание, что в этой простой реализации ключ Committer in comittersCommitsMap, представленный как String в ответе JSON (RestTemplate с этой реализацией также работает):

{
    "commitersList": [
        {
            "name": "name",
            "email": "email"
        }
    ],
    "comittersCommitsMap": {
        "{\"name\":\"name\",\"email\":\"email\"}": 1
    },
    "commitItemsTimeLineMap": {
        "1570929503854": [
            {
                "data": "data"
            }
        ]
    }
}
...