質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

1回答

5823閲覧

Spring Bootの@JsonComponentでLocalDateTime型の値を受け取ろうとしてBindExceptionが発生する

KakeiAkihiko

総合スコア7

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

0クリップ

投稿2019/08/15 07:27

前提・実現したいこと

Spring Bootで、クエリストリングで受け取った日時の値をLocalDateTime型で受け取ろうとしていますが、値を受け取れず、BindExceptionが発生します。

同じように実装しているLocalDate型の変数は日付を受け取ることができます。

LocalDateTimeの場合は特別なやり方があるのでしょうか。

発生している問題・エラーメッセージ

アクセスしたURL: http://localhost:9080/?dateTime=2016/05/11%2012:34:56

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'sampleParameter' on field 'dateTime': rejected value [2016/05/11 12:34:56]; codes [typeMismatch.sampleParameter.dateTime,typeMismatch.dateTime,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sampleParameter.dateTime,dateTime]; arguments []; default message [dateTime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'dateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value '2016/05/11 12:34:56'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2016/05/11 12:34:56]] at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:126) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.19.jar:9.0.19] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.19.jar:9.0.19] // 中略 at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na] 2019-08-15 15:51:15.354 WARN 7076 --- [nio-9080-exec-6] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'sampleParameter' on field 'dateTime': rejected value [2016/05/11 12:34:56]; codes [typeMismatch.sampleParameter.dateTime,typeMismatch.dateTime,typeMismatch.java.time.LocalDateTime,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sampleParameter.dateTime,dateTime]; arguments []; default message [dateTime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDateTime' for property 'dateTime'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value '2016/05/11 12:34:56'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2016/05/11 12:34:56]]]

該当のソースコード

JsonComponents.java

java

1package sample.components; 2 3import java.io.IOException; 4import java.time.LocalDate; 5import java.time.LocalDateTime; 6import java.time.format.DateTimeFormatter; 7 8import org.springframework.boot.jackson.JsonComponent; 9 10import com.fasterxml.jackson.core.JsonGenerator; 11import com.fasterxml.jackson.core.JsonParser; 12import com.fasterxml.jackson.core.JsonProcessingException; 13import com.fasterxml.jackson.databind.DeserializationContext; 14import com.fasterxml.jackson.databind.JsonDeserializer; 15import com.fasterxml.jackson.databind.JsonSerializer; 16import com.fasterxml.jackson.databind.SerializerProvider; 17 18@JsonComponent 19public class JsonComponents { 20 21 // 22 // LocalDateTime 23 // 24 25 static final DateTimeFormatter dateTimePattern = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); 26 27 public static class LocalDateTimeJsonSerializer extends JsonSerializer<LocalDateTime> { 28 29 @Override 30 public void serialize(LocalDateTime value, JsonGenerator generator, SerializerProvider serializers) 31 throws IOException { 32 33 if (value == null) { 34 generator.writeNull(); 35 } else { 36 generator.writeString(dateTimePattern.format(value)); 37 } 38 } 39 } 40 41 public static class LocalDateTimeJsonDeserializer extends JsonDeserializer<LocalDateTime> { 42 43 @Override 44 public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) 45 throws IOException, JsonProcessingException { 46 47 var value = parser.getValueAsString(); 48 49 return value == null ? null : LocalDateTime.parse(value, dateTimePattern); 50 } 51 } 52 53 // 54 // LocalDate 55 // 56 57 static final DateTimeFormatter datePattern = DateTimeFormatter.ofPattern("yyyy/MM/dd"); 58 59 public static class LocalDateJsonSerializer extends JsonSerializer<LocalDate> { 60 61 @Override 62 public void serialize(LocalDate value, JsonGenerator generator, SerializerProvider serializers) 63 throws IOException { 64 65 if (value == null) { 66 generator.writeNull(); 67 } else { 68 generator.writeString(datePattern.format(value)); 69 } 70 } 71 } 72 73 public static class LocalDateJsonDeserializer extends JsonDeserializer<LocalDate> { 74 75 @Override 76 public LocalDate deserialize(JsonParser parser, DeserializationContext context) 77 throws IOException, JsonProcessingException { 78 79 var value = parser.getValueAsString(); 80 81 return value == null ? null : LocalDate.parse(value, datePattern); 82 } 83 } 84}

SampleController.java

java

1package sample.controllers; 2 3import java.time.LocalDate; 4import java.time.LocalDateTime; 5import java.util.LinkedHashMap; 6import java.util.Map; 7 8import org.springframework.web.bind.annotation.GetMapping; 9import org.springframework.web.bind.annotation.ModelAttribute; 10import org.springframework.web.bind.annotation.RestController; 11 12import lombok.Data; 13import lombok.extern.slf4j.Slf4j; 14 15@RestController 16@Slf4j 17public class SampleController { 18 @GetMapping(path = "/") 19 public Map<String, Object> getContents(@ModelAttribute SampleParameter param) { 20 21 log.info("param={}", param); 22 23 var responseBody = new LinkedHashMap<String, Object>(); 24 responseBody.put("succeeded", true); 25 return responseBody; 26 } 27 28 @Data 29 public static class SampleParameter { 30 LocalDateTime dateTime; 31 LocalDate date; 32 } 33}

試したこと

LocalDate型の変数dateもLocalDateTimeと同じように書いていますが、dateの方は問題なく値を受け取れます。

日付を受け取るときのURL: http://localhost:9080/?date=2016/05/11

そのときのレスポンスボディ {"succeeded":true}

補足情報(FW/ツールのバージョンなど)

Spring Boot 2.1.5.RELEASE, Java 11

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

同じように実装しているLocalDate型の変数は日付を受け取ることができます。

とのことですが、LocalDate型のリクエストパラメータを受け取るために特別な実装は不要と思います。確認するために一度JsonComponentsを無効(コメントアウト)にして、LocalDate型が受け取れるか確認してください。
(リクエストボディにJSONフォーマットのデータを受け取る場合は必要かもしれませんが、確認していません)

次にLocalDateTime型のリクエストパラメータですが、こちらは受け取るフィールドに@DateTimeFormatアノテーションを付与することで受け取ることができるようになると思います。

import org.springframework.format.annotation.DateTimeFormat; @Data static class SampleParameter { @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") LocalDateTime dateTime; LocalDate date; }

いちいちLocalDateTime型のフィールドにアノテーションを付与したくないという場合、WebMvcConfigurerを実装した設定クラスでカスタマイズできます。
こうすることでフィールド毎に日付パターンを指定する必要はなくなります。

@Configuration public class Config implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")); registrar.registerFormatters(registry); } }

なお、上記のように日付型データのパターンを決めてしまうと、例えば"2016-05-11 17:25:49"のようなデータを受け取ることができなくなりますのでご留意ください。

投稿2019/08/15 10:19

rubytomato

総合スコア1752

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KakeiAkihiko

2019/08/15 10:48

LocalDateについてご指摘の通りでした。また、LocalDateTimeについても`@DateTimeFormat`で受け取ることができました。ご回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問