【问题标题】:ObjectMapper truncates dates in MapObjectMapper 截断 Map 中的日期
【发布时间】:2019-03-20 17:07:29
【问题描述】:

我有一个ObjectMapper,它配置了以下DateTimeFormatter

DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").withZone(ZoneOffset.UTC);

我想将带有OffsetDateTime 的地图转换为json。我的问题是 OffsetDateTime 显然是正确转换的值

Map<Integer, OffsetDateTime> map = ImmutableMap.of(1, offsetDateTime);
System.out.println(mapper.writeValueAsString(map));
// {"1":"2019-03-20T16:46:00.000+0000"}

但不是当它是关键时

Map<OffsetDateTime, Integer> map = ImmutableMap.of(offsetDateTime, 1);
System.out.println(mapper.writeValueAsString(map).toString());
// {"2019-03-20T16:46Z":1}

请注意,秒和毫秒已被截断。我已经用其他地图(如HashMapTreeMap)对此进行了测试,结果相同。

这是ObjectMapper的定义:

ObjectMapper mapper = new ObjectMapper();

mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.setDateFormat(DateConstants.SIMPLE_DATE_FORMATTER);
mapper.registerModule(new JavaTimeModule());

SimpleModule instantModule = new SimpleModule();
instantModule.addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer());
instantModule.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());

mapper.registerModule(instantModule);
mapper.registerModule(offsetDateTimeModule);

还有这个序列化器:

public class OffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime> {
    @Override
    public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        String str = DateConstants.FORMATTER.format(value);
        gen.writeString(str);
    }
}

在 assylias 回答后编辑:

我现在已将我的SimpleModule 更改为

SimpleModule instantModule = new SimpleModule();
instantModule.addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer());
instantModule.addKeyDeserializer(OffsetDateTime.class, new OffsetDateTimeKeyDeserializer());
instantModule.addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
instantModule.addKeySerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
mapper.registerModule(instantModule);

OffsetDateTimeKeyDeserialzer

public class OffsetDateTimeKeyDeserializer extends KeyDeserializer {
    @Override
    public OffsetDateTime deserializeKey(String key, DeserializationContext ctxt) {
        return OffsetDateTime.from(DateConstants.FORMATTER.parse(key));
    }
}

OffsetDateTime 是值时,事情会继续正常工作。但是,如果它是关键,我现在得到了

Exception in thread "main" com.fasterxml.jackson.core.JsonGenerationException: Can not write a string, expecting field name (context: Object)
    at com.fasterxml.jackson.core.JsonGenerator._reportError(JsonGenerator.java:1961)
    at com.fasterxml.jackson.core.json.JsonGeneratorImpl._reportCantWriteValueExpectName(JsonGeneratorImpl.java:244)
    at com.fasterxml.jackson.core.json.WriterBasedJsonGenerator._verifyValueWrite(WriterBasedJsonGenerator.java:866)
    at com.fasterxml.jackson.core.json.WriterBasedJsonGenerator.writeString(WriterBasedJsonGenerator.java:368)
    at com.brandwatch.signals.commons.util.jackson.OffsetDateTimeSerializer.serialize(OffsetDateTimeSerializer.java:16)
    at com.brandwatch.signals.commons.util.jackson.OffsetDateTimeSerializer.serialize(OffsetDateTimeSerializer.java:12)
    at com.fasterxml.jackson.databind.ser.std.StdKeySerializers$Dynamic.serialize(StdKeySerializers.java:225)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:707)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:639)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3905)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3219)

编辑 2:

这个KeySerializer 似乎为我成功了:

public class OffsetDateTimeKeySerializer extends StdSerializer<Object> {

    public OffsetDateTimeKeySerializer() {
        super(Object.class);
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (value instanceof OffsetDateTime) {
            String str = DateConstants.FORMATTER.format((OffsetDateTime) value);
            gen.writeFieldName(str);
        } else {
            gen.writeFieldName(value.toString());
        }
    }
}

【问题讨论】:

    标签: java serialization java-time objectmapper


    【解决方案1】:

    正如您所发现的,这只会将(反)序列化器应用于值。要将相同的序列化规则应用于密钥,您需要一个密钥(反)序列化器:

    instantModule.addKeyDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer());
    instantModule.addKeySerializer(OffsetDateTime.class, new OffsetDateTimeSerializer());
    

    请注意,密钥反序列化程序需要 KeyDeserializer 对象,而不是 JsonDeserializer

    the javadoc 中的更多信息。

    【讨论】:

    • 谢谢!我已经尝试过了,但是我遇到了另一个问题。我已经扩展了我的问题。
    • @YanickNedderhoff 我不确定是什么导致了错误,但您可能想尝试扩展 StdKeySerializer 而不是重复使用您的 OffsetDateTimeSerlializer
    • 几乎,我已经更新了我的答案(编辑 2)。现在这似乎可以解决问题,我不清楚为什么必须这样。对我来说似乎并不明显:/如果你想过让我知道:)
    • @YanickNedderhoff 我不确定说实话,我以前从未使用过密钥序列化程序。很高兴你最终找到了方法。
    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-06
    相关资源
    最近更新 更多