2016-11-08 75 views
10

我使用的是Jackson 2.8,需要与不允许在ISO 8601时间戳内持续几毫秒的API进行通信。Jackson,java.time,ISO 8601,无序列化

预期的格式是这样的:"2016-12-24T00:00:00Z"

我使用的是杰克逊的JavaTimeModule与WRITE_DATES_AS_TIMESTAMPS设置为false

但是这会打印毫秒。

所以我试图用objectMapper.setDateFormat哪些没有改变任何东西。

我目前的解决方法是这样的:

ObjectMapper om = new ObjectMapper(); 

DateTimeFormatter dtf = new DateTimeFormatterBuilder() 
    .appendInstant(0) 
    .toFormatter(); 

JavaTimeModule jtm = new JavaTimeModule(); 
jtm.addSerializer(Instant.class, new JsonSerializer<Instant>() { 
    @Override 
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { 
     gen.writeString(dtf.format(value)); 
    } 
}); 

om.registerModule(jtm); 

我重写了Instant.class其运作默认的序列。


有没有什么好的方法使用一些配置参数来解决这个问题?

+0

你试过'DateTimeFormatter.ISO_INSTANT'作为格式吗? https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_INSTANT – Gelunox

+0

目标API超出了你的控制范围? –

+1

是的,试过了。它也会打印毫秒。如果我明确创建一个毫秒设置为“0”的“即时”,也许它可以工作。但这对我的用例来说不够安全。它必须**总是**忽略/丢弃串行化的毫秒数。 '......编辑:'是的,API来自外部公司。无控制。 –

回答

3

更新:

只需添加一个@JsonFormat注释蒙山上述Instant财产的日期格式。这很容易。

在这种情况下,你必须与JavaTimeModule像未来的ObjectMapper:

ObjectMapper mapper = new ObjectMapper(); 
mapper.registerModule(new JavaTimeModule()); 

如果你有一个Instant属性的类,你应该添加@JsonFormat注释,并把尚未毫秒的日期模式。它会像下一个:

public static class TestDate { 

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "UTC") 
    Instant instant; 

    //getters & setters 
} 

所以,如果你将对象序列化到JSON它完美的作品:

String json = mapper.writeValueAsString(testDate); 
System.out.println(json); 

输出

{ “瞬间”:“2016-11 -10 06:03:06“}


旧答案。我不知道为什么,但它不起作用:

您可以使用Jackson2ObjectMapperBuilder来构建它。

你只需要添加你想要的dateFormat。它会是这样的未来:

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 

ObjectMapper mapper = Jackson2ObjectMapperBuilder 
      .json()  
      .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 
      .modules(new JavaTimeModule()) 
      .dateFormat(dateFormat) 
      .build(); 
+0

谢谢。稍后再尝试:) –

+0

复制并粘贴您的代码。它不起作用。仍然得到日期+时间+毫秒。 :( –

+0

我用一个解决方案更新了答案 – Pau

3

这是这是一件好事,你可以设置全局的替代,但需要你使用ZonedDateTime即时格式化,因为我们不能设置格式具备Instant串行Java时间模块。

您不会看到使用zoned日期时间的任何副作用,因为jackson会单独序列化区域ID,并且默认情况下处于禁用状态。所以在技术上,这与将格式化程序应用于Instant类似。

当以这种方式使用时,ZonedDateTime序列化器将序列化委托给InstantBaseSerializer并使用指定的自定义格式。

@RunWith(JUnit4.class) 
public class InstantNoMillisTest { 

    private ObjectMapper objectMapper; 

    @Before 
    public void init() { 
     JavaTimeModule module = new JavaTimeModule(); 
     ZonedDateTimeSerializer zonedDateTimeSerializer = new ZonedDateTimeSerializer(new DateTimeFormatterBuilder().appendInstant(0).toFormatter()); 
     module.addSerializer(ZonedDateTime.class, zonedDateTimeSerializer); 
     module.addDeserializer(ZonedDateTime.class, InstantDeserializer.ZONED_DATE_TIME); 

     objectMapper = Jackson2ObjectMapperBuilder.json() 
       .modules(module) 
       .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 
       .build(); 
    } 

    @Test 
    public void serialize() throws IOException { 
     ZonedDateTime zonedDateTime = ZonedDateTime.now(); 
     String noMillis = objectMapper.writeValueAsString(zonedDateTime); 
     System.out.print(noMillis); 
    } 

    @Test 
    public void deserialize() throws IOException { 
     String dateTime = "\"2017-10-26T12:54:59Z\""; 
     ZonedDateTime noMillis = objectMapper.readValue(dateTime, ZonedDateTime.class); 
     System.out.print(noMillis); 
    } 
} 
+0

这使得它非常难以反序列化,因为没有类似的方法来做反向破解 – Whimusical

+0

@Whimusical更新了包含反序列化测试用例的答案。 – Veeram

+0

instantdeserializer在这里工作吗?真的很有趣 – Whimusical