Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

RestTemplate::exchange() - com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60))

Ask Question

Getting the following exception while executing a GET against RestTemplate::exchange(). It's a pretty simple public endpoint. I have included the controller and the DTO. The exception notes 'content type [text/html]' - I have read in other articles that this may be related to error response in XML rather than a response JSON. I have looked a little deeper into exception with no success ... again a pretty simple GET endpoint ?? Any help is appreciated.

Public Endpoint

http://catfact.ninja/fact

Exception

2022-01-07 11:18:40.896 ERROR 22564 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in 
context with path [] threw exception [Request processing failed; nested exception 
is org.springframework.web.client.RestClientException: Error while extracting response 
for type [class com.example.demo.CatDTO] and content type [text/html]; nested exception 
is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, 
Array, Object or token 'null', 'true' or 'false'); nested exception 
is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): 
expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: (PushbackInputStream); line: 1, column: 2]] with root cause

DTO Class

public class CatDTO {
    public String fact;
    public int length;

Controller Class

package com.example.demo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@RestController
public class CatWebServiceController {
    @Autowired
    RestTemplate restTemplate;
    String url = "http://catfact.ninja/fact";
    @RequestMapping(value = "/cat")
    public ResponseEntity<CatDTO> getCatFact() throws IOException {
        System.out.println("Inside: CatWebServiceController::getCatFact()");
        CatDTO catDTO;
        String urlTemplate = UriComponentsBuilder.fromHttpUrl(url)
                .encode()
                .toUriString();
        // Just a little test
        ObjectMapper mapper = new ObjectMapper();
        File f = new File("C:\\macgowan\\project\\test\\demo\\response.json");
        CatDTO catDTO2 = mapper.readValue(f, CatDTO.class);
        // Create the headers
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
        //Add the Jackson Message converter
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        // Note: here we are making this converter to process any kind of response,
        // not only application/*json, which is the default behaviour
        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
        messageConverters.add(converter);
        restTemplate.setMessageConverters(messageConverters);
        ResponseEntity<CatDTO> r = null;
            r = restTemplate.exchange(urlTemplate,
                                      HttpMethod.GET,
                                      entity,
                                      CatDTO.class);
            System.out.println("Inside: It worked");
        catch (HttpClientErrorException exc)
            String errorMessage = exc.getResponseBodyAsString();
            System.out.println("Inside: HttpClientErrorException");
        catch (Exception exc)
            String errorMessage = exc.getMessage();
            System.out.println("Inside: Exception");
        return r;

Spring Init

package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class DemoApplication
    public static void main(String[] args)
        SpringApplication.run(DemoApplication.class, args);
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();

Try https://catfact.ninja/fact instead of http://catfact.ninja/fact

What you are receiving is literally:

<head><title>301 Moved Permanently</title></head> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.20.1</center> </body> </html>

so you where right that your error is related to a response in XML

to quickly find out, you can intercept the response:

 restTemplate.getInterceptors().add((httpRequest, bytes, clientHttpRequestExecution) -> {
            ClientHttpResponse  response=clientHttpRequestExecution.execute(httpRequest, bytes);
            String text = new String(response.getBody().readAllBytes(), StandardCharsets.UTF_8);
            System.out.println(text);
            return response;
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.