รูปแบบ JSON Java 8 LocalDateTime ใน Spring Boot


115

ฉันมีปัญหาเล็กน้อยกับการฟอร์แมต Java 8 LocalDateTime ใน Spring Boot Application ด้วยวันที่ 'ปกติ' ฉันไม่มีปัญหา แต่ฟิลด์ LocalDateTime จะถูกแปลงเป็นดังต่อไปนี้:

"startDate" : {
    "year" : 2010,
    "month" : "JANUARY",
    "dayOfMonth" : 1,
    "dayOfWeek" : "FRIDAY",
    "dayOfYear" : 1,
    "monthValue" : 1,
    "hour" : 2,
    "minute" : 2,
    "second" : 0,
    "nano" : 0,
    "chronology" : {
      "id" : "ISO",
      "calendarType" : "iso8601"
    }
  }

ในขณะที่ฉันต้องการแปลงเป็นสิ่งที่ชอบ:

"startDate": "2015-01-01"

รหัสของฉันมีลักษณะดังนี้:

@JsonFormat(pattern="yyyy-MM-dd")
@DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
public LocalDateTime getStartDate() {
    return startDate;
}

แต่คำอธิบายประกอบข้างต้นไม่สามารถใช้งานได้วันที่ยังคงมีรูปแบบเหมือนด้านบน ข้อเสนอแนะยินดีต้อนรับ!

คำตอบ:


137

อัปเดต : Spring Boot 2.x ไม่ต้องการการกำหนดค่านี้อีกต่อไป ฉันได้เขียนคำตอบล่าสุดไว้ที่นี่แล้ว


(นี่เป็นวิธีการทำก่อน Spring Boot 2.x ซึ่งอาจเป็นประโยชน์สำหรับผู้ที่ทำงานกับ Spring Boot เวอร์ชันเก่า)

ในที่สุดผมก็พบว่าที่นี่วิธีที่จะทำ ในการแก้ไขฉันต้องการการพึ่งพาอื่น:

compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0")

โดยรวมถึงการพึ่งพานี้ฤดูใบไม้ผลิโดยอัตโนมัติจะลงทะเบียนแปลงสำหรับมันตามที่อธิบายไว้ที่นี่ หลังจากนั้นคุณต้องเพิ่มสิ่งต่อไปนี้ใน application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

เพื่อให้แน่ใจว่ามีการใช้ตัวแปลงที่ถูกต้องและวันที่จะถูกพิมพ์ในรูปแบบของ 2016-03-16T13:56:39.492

คำอธิบายประกอบจำเป็นในกรณีที่คุณต้องการเปลี่ยนรูปแบบวันที่เท่านั้น


25
อาจมีค่าควรรวมคำอธิบายประกอบต่อไปนี้ - @JsonSerialize(using = LocalDateTimeSerializer.class)...
Nick Grealy

3
อาจดีกว่าที่จะใช้เพียงapplication.propertiesรายการตามที่แนะนำโดยคำตอบของ @patelb
Membersound

ไม่ทำงาน. แต่คำตอบของ patelib ใช้งานได้จริง!
Mykhaylo Kopytonenko

โปรดทราบว่าเราต้องการ@JsonSerializeคำอธิบายประกอบที่ Nick กล่าวถึงเพื่อให้ใช้งานได้จริง
pennstatephil

92

ฉันเพิ่มการcom.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1อ้างอิงและเริ่มรับวันที่ในรูปแบบต่อไปนี้:

"birthDate": [
    2016,
    1,
    25,
    21,
    34,
    55
  ]

ซึ่งไม่ใช่สิ่งที่ฉันต้องการ แต่ฉันเข้าใกล้มากขึ้น ฉันจึงเพิ่มสิ่งต่อไปนี้

spring.jackson.serialization.write_dates_as_timestamps=false

ไปยังไฟล์ application.properties ซึ่งให้รูปแบบที่ถูกต้องที่ฉันต้องการ

"birthDate": "2016-01-25T21:34:55"

2
ทำงานนอกกรอบเมื่อรวมการjackson-datatype-jsr310พึ่งพา นี่ควรเป็นคำตอบที่ได้รับการยอมรับ
membersound

6
ในฐานะ FYI ส่วน application.properties สามารถทำได้ผ่าน Java config หากคุณกำหนดค่า ObjectMapper ด้วย:mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
justin

31

ที่นี่อยู่ใน maven พร้อมคุณสมบัติเพื่อให้คุณสามารถอยู่รอดระหว่างการอัพเกรดสปริงบูต

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>

1
ใช้วิธีนี้กับความคิดเห็นของ @NickGrealy: อาจคุ้มค่ารวมถึงคำอธิบายประกอบต่อไปนี้ -@JsonSerialize(using = LocalDateTimeSerializer.class)
HairOfTheDog

19

1) การพึ่งพา

 compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.8.8' 

2) คำอธิบายประกอบที่มีรูปแบบวันที่ - เวลา

public class RestObject {

    private LocalDateTime timestamp;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public LocalDateTime getTimestamp() {
        return timestamp;
    }
}

3) การกำหนดค่าสปริง

@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        System.out.println("Config is starting.");
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }
}

2
ขอบคุณมาก. @JsonFormat เป็นคนที่แก้ไขให้ฉัน วิธีแก้ปัญหาข้างต้นไม่ได้ผลสำหรับฉันด้วยเหตุผลบางประการ
โปรแกรมเมอร์

7

ฉันพบโซลูชันอื่นที่คุณสามารถแปลงเป็นรูปแบบใดก็ได้ที่คุณต้องการและนำไปใช้กับประเภทข้อมูล LocalDateTime ทั้งหมดและคุณไม่จำเป็นต้องระบุ @JsonFormat เหนือทุกประเภทข้อมูล LocalDateTime ก่อนอื่นให้เพิ่มการอ้างอิง:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

เพิ่มถั่วต่อไปนี้:

@Configuration
public class Java8DateTimeConfiguration {
    /**
     * Customizing
     * http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
     *
     * Defining a @Bean of type Jackson2ObjectMapperBuilder will allow you to customize both default ObjectMapper and XmlMapper (used in MappingJackson2HttpMessageConverter and MappingJackson2XmlHttpMessageConverter respectively).
     */
    @Bean
    public Module jsonMapperJava8DateTimeModule() {
        val bean = new SimpleModule();

        bean.addDeserializer (ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                return ZonedDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
            }
        });

        bean.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                return LocalDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            }
        });

        bean.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
            @Override
            public void serialize(
                    ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime));
            }
        });

        bean.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
            @Override
            public void serialize(
                    LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime));
            }
        });

        return bean;
    }
}

ในไฟล์กำหนดค่าของคุณเพิ่มสิ่งต่อไปนี้:

@Import(Java8DateTimeConfiguration.class)

สิ่งนี้จะทำให้เป็นอนุกรมและยกเลิกการทำให้เป็นอนุกรมคุณสมบัติทั้งหมด LocalDateTime และ ZonedDateTime ตราบใดที่คุณใช้ objectMapper ที่สร้างโดย spring

รูปแบบที่คุณได้รับสำหรับ ZonedDateTime คือ: "2017-12-27T08: 55: 17.317 + 02: 00 [Asia / Jerusalem]" สำหรับ LocalDateTime คือ: "2017-12-27T09: 05: 30.523"


Probabaly คุณต้องเปลี่ยน val bean เป็น SimpleModule bean = SimpleModule ใหม่ ();
Abhishek Galoda

นี่คือ Java 9?
Daniel Dai

@DanielDai This is in Java 8.
Ida Amit

@ อาบีSimpleModuleขยายความModule. Moduleเป็นนามธรรม val bean = new SimpleModule(); ทำงานได้อย่างสมบูรณ์แบบ
ไอด้าอมิตร

สำหรับ SpringBoot เวอร์ชัน 1.5.3 RELEASE ไม่จำเป็นต้องเพิ่มการพึ่งพา jackson-datatype-jsr310
Moon13

7

การเขียนคำตอบนี้เพื่อเป็นข้อเตือนใจสำหรับฉันเช่นกัน

ฉันรวมคำตอบหลาย ๆ คำไว้ที่นี่และในที่สุดฉันก็ได้ผลกับสิ่งเหล่านี้ (ฉันใช้ SpringBoot 1.5.7 และ Lombok 1.16.16)

@Data
public Class someClass {

   @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
   @JsonSerialize(using = LocalDateTimeSerializer.class)
   @JsonDeserialize(using = LocalDateTimeDeserializer.class)
   private LocalDateTime someDate;

}

1
คุณอาจต้องการเพิ่มการนำเข้าDateTimeFormatและอื่น ๆ
Prayagupd

4

งานนี้ดี:

เพิ่มการอ้างอิง:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

เพิ่มคำอธิบายประกอบ:

@JsonFormat(pattern="yyyy-MM-dd")

ตอนนี้คุณต้องได้รับรูปแบบที่ถูกต้อง

ในการใช้อ็อบเจ็กต์แมปเปอร์คุณต้องลงทะเบียน JavaTime

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

2

ดังที่ได้กล่าวไปแล้ว spring-boot จะดึงข้อมูลทั้งหมดที่คุณต้องการ (สำหรับทั้ง web และ webflux starter)

แต่สิ่งที่ดีไปกว่านั้นคือคุณไม่จำเป็นต้องลงทะเบียนโมดูลด้วยตัวเอง ลองดูที่นี่ เนื่องจากการ@SpringBootApplicationใช้งาน@EnableAutoConfigurationภายใต้ประทุนหมายความว่าJacksonAutoConfigurationจะถูกเพิ่มลงในบริบทโดยอัตโนมัติ ตอนนี้ถ้าคุณมองเข้าไปข้างในJacksonAutoConfigurationคุณจะเห็น:

    private void configureModules(Jackson2ObjectMapperBuilder builder) {
        Collection<Module> moduleBeans = getBeans(this.applicationContext,
                Module.class);
        builder.modulesToInstall(moduleBeans.toArray(new Module[0]));
    }

Fella นี้จะถูกเรียกในกระบวนการเริ่มต้นและจะดึงโมดูลทั้งหมดที่สามารถพบได้ในคลาสพา ธ (ฉันใช้ Spring Boot 2.1)


1

ฉันใช้ Springboot 2.0.6 และด้วยเหตุผลบางประการการเปลี่ยนแปลง yml ของแอปไม่ทำงาน และฉันมีข้อกำหนดเพิ่มเติม

ฉันพยายามสร้าง ObjectMapper และทำเครื่องหมายเป็น Primary แต่ spring boot บ่นว่าฉันมี jacksonObjectMapper แล้วว่าเป็น Primary !!

นี่คือสิ่งที่ฉันทำ ฉันทำการเปลี่ยนแปลงตัวทำแผนที่ภายใน

Serializer และ Deserializer ของฉันมีความพิเศษ - พวกเขาจัดการกับ 'dd / MM / YYYY'; และในขณะที่การแยกอนุกรม - พยายามอย่างดีที่สุดที่จะใช้รูปแบบยอดนิยม 3-4 รูปแบบเพื่อให้แน่ใจว่าฉันมี LocalDate อยู่บ้าง

@Autowired
ObjectMapper mapper;

@PostConstruct
public ObjectMapper configureMapper() {
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

    mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, true);
    mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

    SimpleModule module = new SimpleModule();
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
    module.addSerializer(LocalDate.class, new LocalDateSerializer());
    mapper.registerModule(module);

    return mapper;
}

0

@JsonDeserialize(using= LocalDateDeserializer.class) ไม่ได้ผลสำหรับฉันด้วยการพึ่งพาด้านล่าง

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version> 2.9.6</version>
</dependency>

ฉันได้ใช้ตัวแปลงรหัสด้านล่างเพื่อแยกวันที่ออกเป็นjava.sql.Dateไฟล์.

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;


@SuppressWarnings("UnusedDeclaration")
@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<java.time.LocalDate, java.sql.Date> {


    @Override
    public java.sql.Date convertToDatabaseColumn(java.time.LocalDate attribute) {

        return attribute == null ? null : java.sql.Date.valueOf(attribute);
    }

    @Override
    public java.time.LocalDate convertToEntityAttribute(java.sql.Date dbData) {

        return dbData == null ? null : dbData.toLocalDate();
    }
}

0

เพียงใช้:

@JsonFormat(pattern="10/04/2019")

หรือคุณสามารถใช้รูปแบบได้ตามต้องการเช่น: ('-' in place of '/')


คำตอบนี้ได้รับการแชร์มาก่อน แต่จะใช้ไวยากรณ์ที่ถูกต้อง
Erik Pragt

0

ฉันใช้ Spring Boot 2.1.8 ฉันได้นำเข้า

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

ซึ่งรวมถึงแจ็คสันประเภทข้อมูล-jsr310

จากนั้นฉันต้องเพิ่มคำอธิบายประกอบเหล่านี้

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("date")
LocalDateTime getDate();

และใช้งานได้ JSON มีลักษณะดังนี้:

"date": "2020-03-09 17:55:00"

คำแนะนำ: spring-boot-starter-jsonจำเป็นต้องรวมเฉพาะในกรณีที่spring-boot-starter-webขาดหายไป
judomu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.