我将我的java项目升级到jdk17,spring 6.1和springboot 3.3
我得到了
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalTime` from String "7:45PM": Failed to deserialize java.time.LocalTime: (java.time.format.DateTimeParseException) Text '7:45PM' could not be parsed at index 4
修复我的junits时出错
可重现的代码-
public static ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false);
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
return objectMapper;
}
public static <T> T getJsonFixtureFromString(String jsonString, Class<T> clazz) {
return getObjectMapper().readValue(jsonString, clazz);
}
@Test
public void myTest(){
String s = "{\"name\":\"Evening Restriction\",\"restrictions\":[{\"starttime\":\"7:45PM\",\"endtime\":\"8:15PM\"}]}"
RestrictProfileDTO dto = getJsonFixtureFromString(s, RestrictProfileDTO.class);
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestrictProfileDTO {
@JsonProperty(value = "name")
private String name;
@JsonProperty(value = "restrictions")
private List<RestrictionDTO> restrictions;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestrictionDTO {
@JsonProperty(value = "starttime")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "h:mma")
private LocalTime startTime;
@JsonProperty(value = "endtime")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "h:mma")
private LocalTime endTime;
}
完整日志-
java.lang.RuntimeException: Can't create class RestrictProfileDTO from json {"name":"Evening Restriction","restrictions":[{"restrict_day":"2","starttime":"7:45PM","endtime":"8:15PM"}]} .
at TestUtils.getJsonFixtureFromString(TestUtils.java:79)
at CallMenuRestrictionResourceTest.testCreateProfileBadRequestIllegalArgumentException(CallMenuRestrictionResourceTest.java:110)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:76)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:108)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy2/jdk.proxy2.$Proxy5.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalTime` from String "7:45PM": Failed to deserialize java.time.LocalTime: (java.time.format.DateTimeParseException) Text '7:45PM' could not be parsed at index 4
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 130] (through reference chain: RestrictProfileDTO["restrictions"]->java.util.ArrayList[0]->RestrictionDTO["starttime"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1958)
at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1245)
at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:176)
at com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer._fromString(LocalTimeDeserializer.java:195)
at com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer.deserialize(LocalTimeDeserializer.java:109)
at com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer.deserialize(LocalTimeDeserializer.java:37)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:399)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4905)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3848)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3816)
at TestUtils.getJsonFixtureFromString(TestUtils.java:77)
... 52 more
Caused by: java.time.format.DateTimeParseException: Text '7:45PM' could not be parsed at index 4
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
at java.base/java.time.LocalTime.parse(LocalTime.java:465)
at com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer._fromString(LocalTimeDeserializer.java:193)
... 68 more
我使用的是 com.fasterxml.jackson.core 2.16.0 版本,我将其更改为最新稳定版本 2.17.0,但错误仍然没有解决。我预计也许更高版本可以兼容jdk17。但错误并没有解决。
当我将传递给 getJsonFixtureFromString() 方法的字符串中的大写 PM 更改为小 pm 时,错误得到解决。但如何在不更改字符串的情况下解决这个问题。
在jdk11中没有出现这个错误。
您可以尝试创建自定义解串器。
@JsonDeserialize(using = CaseInsensitiveLocalTimeDeserializer.class)
@JsonProperty(value = "starttime")
private LocalTime startTime;
@JsonDeserialize(using = CaseInsensitiveLocalTimeDeserializer.class)
@JsonProperty(value = "endtime")
private LocalTime endTime;
与:
public class CaseInsensitiveLocalTimeDeserializer extends JsonDeserializer<LocalTime> {
private final DateTimeFormatter formatter;
public CaseInsensitiveLocalTimeDeserializer() {
this.formatter = DateTimeFormatter.ofPattern("h:mma").withLocale(Locale.ENGLISH);
}
@Override
public LocalTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String timeStr = p.getText().toUpperCase(); // Convert to upper case to handle AM/PM consistently
try {
return LocalTime.parse(timeStr, formatter);
} catch (DateTimeParseException e) {
throw new InvalidFormatException(p, "Failed to parse time", timeStr, LocalTime.class);
}
}
}
添加解串器时映射器看起来像这样。
public static ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalTime.class, new CaseInsensitiveLocalTimeDeserializer());
objectMapper.registerModule(javaTimeModule);
// Other configurations...
return objectMapper;
}