【发布时间】:2022-01-07 06:04:22
【问题描述】:
我有以下对象:
@Validated
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
@Schema(description = "Request")
public final class Request implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty("date")
@Schema(description = "Date")
private OffsetDateTime date;
}
我将此对象作为休息控制器的响应发送:
@RestController
public class RequestController {
@RequestMapping(
value = "/requests",
produces = {"application/json;charset=UTF-8"},
consumes = {"application/json"},
method = RequestMethod.POST)
public ResponseEntity<Request> get() {
LocalDate date = LocalDate.of(2021, Month.OCTOBER, 22);
OffsetDateTime dateTime = date.atTime(OffsetTime.MAX);
Request request = new Request(dateTime);
return ResponseEntity.ok(request);
}
}
但我有配置:
@Configuration
public class WebConfiguration implements ServletContextInitializer, WebMvcConfigurer {
private final List<FilterRegistration> filterRegistrations;
private final ApplicationContext applicationContext;
public WebConfiguration(List<RestApplicationInstaller> restApplicationInstallers,
List<MonitoringRestApplicationInstaller> monitoringRestApplicationInstallers,
List<FilterRegistration> filterRegistrations,
ApplicationContext applicationContext) {
this.filterRegistrations = filterRegistrations;
this.applicationContext = applicationContext;
}
@Override
public void onStartup(ServletContext servletContext) {
VersionServletInstaller.installServlets(servletContext, getRegisterAsyncService(servletContext));
filterRegistrations.forEach(filterRegistration -> filterRegistration.onApplicationEvent(new ContextRefreshedEvent(applicationContext)));
}
private RegisterAsyncService getRegisterAsyncService(final ServletContext servletContext) {
final WebApplicationContext ctx = getWebApplicationContext(servletContext);
final RegisterAsyncService registerAsyncService = Objects.requireNonNull(ctx).getBean(RegisterAsyncService.class);
registerAsyncService.exec();
return registerAsyncService;
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer(CustomAnnotationIntrospector customAnnotationIntrospector) {
return builder -> builder.serializationInclusion(NON_NULL)
.annotationIntrospector(customAnnotationIntrospector);
}
}
好的。
所以...我得到date 字段作为响应:
2021-10-21T23:59:59.999999999-18:00
当我测试我的控制器时,我尝试获得响应,将其反序列化为 Request 对象并检查匹配:
@DirtiesContext
@SpringBootTest(
classes = {WebConfiguration.class, JacksonAutoConfiguration.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
@EnableWebMvc
class RequestControllerTest {
private static final CharacterEncodingFilter
CHARACTER_ENCODING_FILTER = new CharacterEncodingFilter();
static {
CHARACTER_ENCODING_FILTER.setEncoding(DEFAULT_ENCODING);
CHARACTER_ENCODING_FILTER.setForceEncoding(true);
}
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@Autowired
private WebApplicationContext context;
@PostConstruct
private void postConstruct() {
this.mockMvc =
MockMvcBuilders
.webAppContextSetup(this.context)
.addFilters(CHARACTER_ENCODING_FILTER)
.build();
}
@Test
void requestByIdTest() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.post("/requests")
.characterEncoding(CHARACTER_ENCODING_FILTER)
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(
result -> Assertions.assertEquals(mapToObject(result.getResponse().getContentAsString(Charset.forName(CHARACTER_ENCODING_FILTER)), Request.class), getExpectedRequest()));
}
private WebComplianceRequest getExpectedRequest() {
LocalDate date = LocalDate.of(2021, Month.OCTOBER, 22);
OffsetDateTime dateTime = date.atTime(OffsetTime.MAX);
Request request = new Request(dateTime);
}
private <T> T mapToObject(String json, Class<T> targetClass) {
try {
return getReaderForClass(targetClass).readValue(json);
} catch (IOException e) {
throw new RuntimeExsception(e);
}
}
private <T> ObjectReader getReaderForClass(Class<T> targetClass) {
return objectMapper.readerFor(targetClass);
}
}
但我得到一个例外,因为预期对象和得到对象中的date 字段不同:
Date in response: 2021-10-22T17:59:59.999999999Z
Expected date: 2021-10-21T23:59:59.999999999-18:00
为什么会这样?
为什么出现Z 而不是时区?为什么日期从2021-10-21 更改为2021-10-22?我该如何解决?
我没有得到任何异常,我得到 匹配失败,因为当我匹配响应和预期对象时日期不同。我只是用标准ObjectMapper 反序列化对象并检查与equals() 匹配的对象。
【问题讨论】:
-
发布代码输出上述输出,以便我们可以重现。见minimal reproducible example
-
好吧,
2021-10-21T23:59:59.999999999-18:00与 UTC 的偏移量为-18:00小时,而2021-10-22T17:59:59.999999999Z与 UTC 中的Instant相同(Z表示 Zulu / UTC)。两者之间的差异是 18 小时。 您在同一时间有两种不同的表示(偏移量)。 -
问题需要包括您编写的将这个东西序列化为 JSON 的代码(大概,当您用它标记它时,使用 Jackson),以及您如何反序列化它。
-
您有日期和时间
2021-10-21T23:59:59.999999999,您将其定义为偏移-18:00(由atTime(OffsetTime.MAX)。这意味着您基本上必须 添加 18 小时才能在 UTC 中获得同一时刻的表示(偏移量为+00:00或只是Z),这会导致不同的一天,因为午夜前的时刻增加了 18 小时,这将变成第二天的时间。 -
@OleV.V.或者我们可以关闭并删除代码示例不一致的不完整问题。
标签: java json datetime jackson offsetdatetime