首页 > 解决方案 > 使用 XMLGregorianCalendar 时的 Spring REST 时区问题

问题描述

我有一个接受并提供 json 输出的 spring rest 服务。

@PostMapping(path = "/path", consumes = {"application/json"}, produces = {"application/json"})
public ResponseEntity<RequestData> method(@RequestBody RequestData request){
    return request;
}

RequestData 包含多个日期 (XMLGregorianCalendar)。我无法更改类型,因为它是从 xsd 生成的。为了获取原始时区的日期,我使用了参数 spring.jackson.date-format: yyyy-MM-dd'T'HH:mm:ssZ

要求

{
    "date1":"2020-02-28T09:26:59+09:00",
    "date2":"2020-01-10T12:46:29+04:00",
    "date3":"2020-03-15T11:32:43+08:00"
}

从这个请求中,我得到了一个具有不同时区的 XMLGregorianCalendar。但是在发送响应消息时,日期将转换为 0 时区。回复

{
    "date1":"2020-02-28T00:26:59+0000",
    "date2":"2020-01-10T08:46:29+0000",
    "date3":"2020-03-15T03:32:43+0000"
}

需要对杰克逊进行哪些设置才能在响应中获得非零时区?请求中返回的响应时区是必要的。或者杰克逊不知道如何做到这一点并且总是将日期转换为单个时区?在那种情况下,使用哪个库?谢谢!


解决方案

您必须创建一个序列化器和反序列化器。然后你需要覆盖现有的 ObjectMapper。如果只重写了序列化器,那么在接收到数据后,时区将被归一化(减少到+00:00),因此也需要重写反序列化器。序列化器:

public class XMLGCSerializer extends JsonSerializer<XMLGregorianCalendar> {

    @Override
    public void serialize(XMLGregorianCalendar value,
                          JsonGenerator gen,
                          SerializerProvider serializers)
            throws IOException {
        gen.writeObject(value.toString());
    }
}

解串器:

public class XMLGCDeserializer extends JsonDeserializer<XMLGregorianCalendar> {

    @Override
    public XMLGregorianCalendar deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        String stringDate = parser.getText();
        try {
            return DatatypeFactory.newInstance().newXMLGregorianCalendar(stringDate);
        } catch (Exception e) {
            throw new RuntimeException(e);
            //or return null
        }
    }
}

覆盖对象映射器

@Component
public class JacksonConfig {

    private final ObjectMapper objectMapper;

    public JacksonConfig() {
        objectMapper = new ObjectMapper();
        SimpleModule s = new SimpleModule();
        s.addSerializer(XMLGregorianCalendar.class, new XMLGCSerializer());
        s.addDeserializer(XMLGregorianCalendar.class, new XMLGCDeserializer());
        objectMapper.registerModule(s);
    }

    @Bean
    public ObjectMapper getContext() {
        return objectMapper;
    }
}

标签: javajsonspringrest

解决方案


您可以创建一个单独的类来自己处理序列化。这是一个例子:

class XMLGCSerializer extends JsonSerializer<XMLGregorianCalendar> {

    @Override
    public void serialize(XMLGregorianCalendar value, 
            JsonGenerator gen, 
            SerializerProvider serializers)
            throws IOException {
        gen.writeObject(value.toString());
    }

}

现在你只需要注释你的字段RequestData

class RequestData{
    @JsonSerialize(using = XMLGCSerializer.class)
    XMLGregorianCalendar date1;
    //...
}

推荐阅读