概要
- RestTemplate で HTTP レスポンスの JSON とマッピングするクラスが合わない場合に発生する例外を調査する
環境
- Spring Boot 2.3.1 (Spring Framework 5.2.7 + Jackson Databind 2.11.0)
- Java 11 (AdoptOpenJDK 11.0.7+10)
- Gradle 6.5
- macOS 10.15.5 Catalina
検証用コード
ファイル一覧
├── build.gradle
└── src
└── main
└── java
└── com
└── example
├── DemoApplication.java
└── MyResponse.java
build.gradle
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
src/main/java/com/example/MyResponse.java
JSON をマッピングするクラス。
package com.example;
import java.util.List;
public class MyResponse {
public String name;
public List<MyData> list; // ここに想定外の値をマッピング試行した際の例外を調べる
public static class MyData {
public String foo;
public String bar;
}
}
src/main/java/com/example/DemoApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
import java.util.Map;
@SpringBootApplication
@RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
// OK: JSON を返却
@GetMapping("/ok")
public Map ok() {
return
Map.of(
"name", "myname",
"list", List.of(
Map.of("foo", "myfoo", "bar", "mybar")
)
);
}
// NG1: MyResponse に合わない JSON を返却
@GetMapping("/ng1")
public Map ng1() {
return
Map.of(
"name", "myname",
"list", "mylist" // List があるべき場所に文字列 "mylist"
);
}
// NG2: MyResponse に合わない JSON を返却
@GetMapping("/ng2")
public Map ng2() {
return
Map.of(
"name", "myname",
"list", "" // List があるべき場所に空文字列
);
}
// NG3: MyResponse に合わない JSON を返却
@GetMapping("/ng3")
public Map ng3() {
return
Map.of(
"name", "myname",
"list", List.of("mylist") // MyData があるべき場所に文字列 "mylist"
);
}
// NG4: MyResponse に合わない JSON を返却
@GetMapping("/ng4")
public Map ng4() {
return
Map.of(
"name", "myname",
"list", List.of("") // MyData があるべき場所に空文字列
);
}
@GetMapping("/mydata/{path}")
public MyResponse mydata(@PathVariable("path") String path) throws Exception {
try {
// 自らのサーバから JSON を取得
String uri = "http://localhost:8080/" + path;
RequestEntity re = RequestEntity.get(new URI(uri)).build();
RestTemplate restTemplate = new RestTemplate();
// JSON を MyResponse に変換
// JSON が MyResponse に合わなければここで例外が発生する
ResponseEntity<MyResponse> res = restTemplate.exchange(re, MyResponse.class);
return res.getBody();
} catch (Exception e) {
// どういう例外が発生しているか出力する
System.out.println("例外クラス: " + e.getClass().getName());
// 原因となった例外のチェーンを出力する
Throwable cause = e;
while ((cause = cause.getCause()) != null) {
System.out.println("原因例外クラス: " + cause.getClass().getName());
}
throw e;
}
}
}
Spring Boot アプリケーションを起動
$ gradle bootRun
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.1.RELEASE)
検証
他のコンソールから curl で HTTP リクエストを投げて、どのような例外が発生するかを検証する。
OK: MyResponse にマッピングできる JSON
$ curl http://localhost:8080/ok
{"list":[{"foo":"myfoo","bar":"mybar"}],"name":"myname"}
$ curl http://localhost:8080/mydata/ok
{"name":"myname","list":[{"foo":"myfoo","bar":"mybar"}]}
NG1: List があるべき場所に文字列 "mylist"
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng1
{"list":"mylist","name":"myname"}
JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng1
{"timestamp":"2020-06-22T21:05:59.317+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng1"}
Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException
原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException
原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException
2020-06-23 06:05:59.311 ERROR 4196 --- [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.MyResponse] and content type [application/json];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token;
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token
at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])] with root cause
Spring Boot サーバ側のログ (出力そのまま)。
例外クラス: org.springframework.web.client.RestClientException
原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException
原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException
2020-06-23 06:05:59.311 ERROR 4196 --- [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.MyResponse] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token
at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])] with root cause
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<com.example.MyResponse$MyData>` out of VALUE_STRING token
at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1464) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1238) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1190) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.java:331) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:264) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482) ~[jackson-databind-2.11.0.jar:2.11.0]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3487) ~[jackson-databind-2.11.0.jar:2.11.0]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:269) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:257) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:998) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:981) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:741) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:641) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at com.example.DemoApplication.mydata(DemoApplication.java:86) ~[main/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
NG2: List があるべき場所に空文字列
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng2
{"list":"","name":"myname"}
JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng2
{"timestamp":"2020-06-22T21:07:21.986+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng2"}
Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException
原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException
原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException
2020-06-23 06:07:21.984 ERROR 4196 --- [nio-8080-exec-4] 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.MyResponse] and content type [application/json];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of `java.util.ArrayList` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('');
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot construct instance of `java.util.ArrayList` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('')
at [Source: (PushbackInputStream); line: 1, column: 25] (through reference chain: com.example.MyResponse["list"])] with root cause
NG3: MyData があるべき場所に文字列 "mylist"
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng3
{"list":["mylist"],"name":"myname"}
JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng3
{"timestamp":"2020-06-22T21:08:08.316+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng3"}
Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException
原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException
原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException
2020-06-23 06:08:08.315 ERROR 4196 --- [nio-8080-exec-6] 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.MyResponse] and content type [application/json];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('mylist');
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('mylist')
at [Source: (PushbackInputStream); line: 1, column: 26] (through reference chain: com.example.MyResponse["list"]->java.util.ArrayList[0])] with root cause
NG4: // MyData があるべき場所に空文字列
クラスにマッピング試行する JSON データ。
$ curl http://localhost:8080/ng4
{"list":[""],"name":"myname"}
JSON データをクラスにマッピングする処理をする URL にアクセス。
$ curl http://localhost:8080/mydata/ng4
{"timestamp":"2020-06-22T21:08:36.354+00:00","status":500,"error":"Internal Server Error","message":"","path":"/mydata/ng4"}
Spring Boot サーバ側のログ (読みやすくするため一部を抜粋して整形したもの)。
例外クラス: org.springframework.web.client.RestClientException
原因例外クラス: org.springframework.http.converter.HttpMessageNotReadableException
原因例外クラス: com.fasterxml.jackson.databind.exc.MismatchedInputException
2020-06-23 06:08:36.352 ERROR 4196 --- [nio-8080-exec-8] 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.MyResponse] and content type [application/json];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('');
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot construct instance of `com.example.MyResponse$MyData` (although at least one Creator exists):
no String-argument constructor/factory method to deserialize from String value ('')
at [Source: (PushbackInputStream); line: 1, column: 26] (through reference chain: com.example.MyResponse["list"]->java.util.ArrayList[0])] with root cause
発生している例外の公式資料
org.springframework.web.client.RestClientException
RestClientException はサーバのエラーレスポンス、入出力エラー、レスポンスをデコードする際のエラーなどで発生する例外。
RestClientException (Spring Framework 5.2.7.RELEASE API)
Base class for exceptions thrown by RestTemplate in case a request fails because of a server error response, as determined via ResponseErrorHandler.hasError(ClientHttpResponse), failure to decode the response, or a low level I/O error.
org.springframework.http.converter.HttpMessageNotReadableException
親クラスの HttpMessageConversionException の説明によると変換に失敗した際に投げられる例外とのこと。
HttpMessageNotReadableException (Spring Framework 5.2.7.RELEASE API)
Thrown by HttpMessageConverter implementations when the HttpMessageConverter.read(java.lang.Class extends T>, org.springframework.http.HttpInputMessage) method fails.
HttpMessageConversionException (Spring Framework 5.2.7.RELEASE API)
Thrown by HttpMessageConverter implementations when a conversion attempt fails.
com.fasterxml.jackson.databind.exc.MismatchedInputException
マッピングするクラス定義に合わない JSON が来たときに発生する例外。
MismatchedInputException (jackson-databind 2.11.0 API)