Java 8 LocalDate Jackson biçimi


139

İçin java.util.Date bulduğumda

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")  
  private Date dateOfBirth;

sonra JSON isteğinde gönderdiğimde

{ {"dateOfBirth":"01/01/2000"} }  

işe yarıyor.

Bunu Java 8'in LocalDate alanı için nasıl yapmalıyım ?

Sahip olmayı denedim

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;  

İşe yaramadı.

Lütfen birisi bunu yapmanın doğru yolunun ne olduğunu bana bildirebilir mi ..

Bağımlılıklar aşağıdadır

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>jaxrs-api</artifactId>
     <version>3.0.9.Final</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.2</version>
</dependency>
<dependency>
    <groupId>com.wordnik</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>1.3.10</version>
</dependency>

Yanıtlar:


106

Ek açıklamaları kullanarak bunu asla basit bir şekilde gerçekleştiremedim. Çalışmasını sağlamak ContextResolveriçin ObjectMapper, bir for oluşturdum , sonra JSR310Module( güncelleme: şimdi onun JavaTimeModuleyerine ) ekledim ve bir uyarı daha ekledim, bu da tarih olarak yazma damgasını yanlış olarak ayarlama ihtiyacıydı. JSR310 modülünün belgelerinde daha fazlasını görün . İşte kullandığım şeyin bir örneği.

Bağımlılık

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

Not: Bununla karşılaştığım bir sorun jackson-annotation, başka bir bağımlılık tarafından çekilen sürümün 2.3.2 sürümünü kullanmasıdır ve bu jsr310,. Olan ObjectIdResolver, bir 2.4 sınıfı olan NoClassDefFound'um oldu . Bu yüzden dahil edilen bağımlılık sürümlerini sıraya koymam gerekiyordu

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        // Now you should use JavaTimeModule instead
        MAPPER.registerModule(new JSR310Module());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return MAPPER;
    }  
}

Kaynak sınıfı

@Path("person")
public class LocalDateResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getPerson() {
        Person person = new Person();
        person.birthDate = LocalDate.now();
        return Response.ok(person).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createPerson(Person person) {
        return Response.ok(
                DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
    }

    public static class Person {
        public LocalDate birthDate;
    }
}

Ölçek

curl -v http://localhost:8080/api/person
Sonuç: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person
Sonuç: 2015-03-01


JAXB çözümü için buraya da bakın .

GÜNCELLEME

JSR310ModuleJackson sürümüne 2.7 itibariyle kullanım dışı kalmıştır. Bunun yerine modülü kaydetmelisiniz JavaTimeModule. Hala aynı bağımlılıktır.


1
Merhaba Peeskillet, doğumDate alanı "doğumDate" olarak oluşturuluyor: {"year": 0, "month": "Month", "dayOfMonth": 0, "dayOfWeek": "DayOfWeek", "era": {" value ": 0}," dayOfYear ": 0," leapYear ": false," monthValue ": 0," chronology ": {" id ":" "," calendarType ":" "}} bunu nasıl düzeltebilirim "doğumTarihi" olarak ???
JAB

ContextResolver'ın çağrıldığını kontrol edin. getContextYönteme bir print ifadesi ekleyin . Bu yöntem çağrılırsa, bunun işe yaramaması için bir neden göremiyorum. Çağrılmamışsa, uygulama yapılandırmasıyla düzeltilmesi gereken bir şey olabilir. Bunun için sağladığınızdan daha fazlasını görmem gerekiyor. Resteasy sürümü, bağımlılıklar, web.xml veya Uygulama alt sınıfı uygulama yapılandırması gibi. Temelde sorunu yeniden oluşturmak için yeterli
Paul Samsotha

ContextResolver, Peeskillet olarak adlandırılmıyor. Bunu web.xml'de <context-param> <param-name> resteasy.resources </param-name> <param-value> com.bac.ObjectMapperContextResolver </param-value> </context-param> olarak yeniden kaydediyorum kullandığım bağımlılıklar için güncellenmiş soru
JAB

Sorun Swagger gibi görünüyor. Devre dışı bırakmayı söyleyebilirim, ancak bu sorudan yola çıkarak Swagger'ın ObjectMapper ile kendi nesnenizi kullanmaya çalışmak arasında bir çelişki olan bir sorun var. Onlarınkini deneyebilir ve devre dışı bırakabilirsiniz ve ContextResolver'da, tüm yapılandırmaları swagger'ın yaptığı gibi ayarlayın ObjectMapper(soruda bir bağlantı görebilirsiniz). Swagger ile pek çalışmadığım için bilmiyorum. Ama bence asıl sorun swagger, neden bağlam çözümleyici çağrılmıyor.
Paul Samsotha

Ayrıca testlerden sonra, açıklama yapar işi. Biz bile var Swagger ObjectMapper kullanmak, daha sonra zaten bizim için false ile zaman damgalarını yapılandırın. Yani bu çalışmalı. Daha iyi yardım için, sorunu gösteren bir MCVE sağlamanızı şiddetle tavsiye ederim .
Paul Samsotha

95

@JsonSerialize ve @JsonDeserialize benim için iyi çalıştı. Ek jsr310 modülünü içe aktarma ihtiyacını ortadan kaldırırlar:

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;

Deserializer:

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    protected LocalDateDeserializer() {
        super(LocalDate.class);
    }


    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        return LocalDate.parse(jp.readValueAs(String.class));
    }

}

serializer:

public class LocalDateSerializer extends StdSerializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    public LocalDateSerializer(){
        super(LocalDate.class);
    }

    @Override
    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider sp) throws IOException, JsonProcessingException {
        gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
    }
}

2
Bu benim için en iyi cevap. Teşekkürler!
Romeo Jr Maranan

7
Bu sınıflar dahildir jackson-datatype-jsr310. Bunları projenizde manuel olarak tanımlamanıza gerek yok.
NeuroXc

1
Bu çözüm, içindeki serileştiricileri kullanarak benim için çalıştı jackson-datatype-jsr310.
dave

2
Bu yeni en iyi cevap olmalı.
Doktor Parametresi

1
Jackson-datatype-jsr310'da serileştiriciler ve seriyi kaldırıcılar kullanıyorsanız, alanınıza @JsonFormat (şekil = JsonFormat.Shape.STRING) eklemeniz daha iyi olur. Bu format olmadan, değer serileştirme çalışmasına rağmen [yıl, ay, gün] olarak serileştirilecektir.
Jian Chen

74
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

benim için iyi çalışıyor.


2
new com.fasterxml.jackson.datatype.jsr310.JSR310Module()Jackson'ın 2.5.4 sürümü için. JavaTimeModule sınıfı bu sürümde mevcut değil.
user3774109

Bu cevap aynı zamanda LocalDateTime(jackson 2.9.5) için de geçerlidir . 1 ek bağımlılık gerekli, bu yüzden build.sbt'm şöyle görünüyor:"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.5", "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.9.5"
ruhong

2
Bunun çok daha fazla oyu olmalı. Basit ve etkili.
mkasberg

Mükemmel çalıştı! Teşekkür ederim.
MAD-HAX

Bu beni doğru yönü gösterdi, teşekkürler! Spring-boot'ta yapmanız gereken tek şeyin application.properties'e aşağıdakileri eklemek olduğunu ekleyeceğim: spring.jackson.serialization.write-dates-as-timestamps = false
Joost Lambregts

46

Jackson ve JSR 310 sürüm "2.8.5" ile Spring Boot web uygulamasında

compile "com.fasterxml.jackson.core:jackson-databind:2.8.5"
runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.5"

@JsonFormateserleri:

import com.fasterxml.jackson.annotation.JsonFormat;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate birthDate;

2
Bu seriyi kaldırma için işe yarar mı? yoksa sadece serileştirme mi?
Seriyi kaldırma

7
@JsonDeserialize(using= LocalDateDeserializer.class)
Seri durumdan çıkarıcıyı

1
açık farkla en basit!
Antonio

@JsonFormatsadece çıktı veri formatını değiştirmek için. stackoverflow.com/a/53251526/816759@JsonFormat , ile mükemmel çalışır @JsonDeserialize,@JsonSerialize
Baha

Eğer JSR310 bağımlılık ekledikten sonra Bahar Boot olarak, yapmanız gereken tek şey eklemektir spring.jackson.serialization.write-dates-as-timestamps=falseadresinden Müşteri application.propertiesve bunu biçimlendirir yyyy-MM-ddotomatik. Gerek yok@JsonFormat
esfandia

31

En basit çözüm (serileştirmeyi ve serileştirmeyi de destekler)

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate dateOfBirth;

Projenizde aşağıdaki bağımlılıkları kullanırken.

Uzman

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.9.7</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.datatype</groupId>
   <artifactId>jackson-datatype-jsr310</artifactId>
   <version>2.9.7</version>
</dependency>

Gradle

compile "com.fasterxml.jackson.core:jackson-databind:2.9.7"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7"

ContextResolver, Serializer veya Deserializer için ek bir uygulama gerekmez.


Parlak, uzak ve uzak en kolayı. Bilginize, çok sayıda bağımlılığı olan herkes için, jackson notlarını içeren diğer kütüphaneleri güncellemem gerekiyordu.
brt

Bu cevap, sorunumu çözmem için en yakın olanı. Serileştirme çalışıyor, ancak bence @JsonFormat ile kullandığım model nedeniyle seriyi kaldırma başarısız oluyor (@JsonFormat (şekil = JsonFormat.Shape.STRING, desen = "gg-AA-yyyy_HH: mm: SS").
fsakiyama

Eğer başarısız bir deserialization varsa, büyük olasılıkla sizin olduğunu ObjectMappersahip değil JavaTimeModulekaydetti. ObjectMapper örneğiniz spring / MessageConverter çerçevesinden sağlandıysa. Onları bağlamak için biraz sihir yaptılar. Diğer durumda, POJO'daki tüm "YerelTarih" için varsayılan registerModuleolarak etkinleştirilmelidirLocalDateDeserializer
Dennis C

19

Yana LocalDateSerializerziyade "yıl-ay-gün" varsayılan olarak (bir json dize), daha ben herhangi bir özel ihtiyaç istemiyorum çünkü içine dönüşler bunu "[yıl, ay, gün]" (bir json dizisi) ObjectMapperyapabilirsiniz (setup yapmak LocalDateSerializersen devre dışı eğer dizeleri oluşturmak SerializationFeature.WRITE_DATES_AS_TIMESTAMPSama bu senin için ek kurulum gerektirir ObjectMapper, ben aşağıdakileri kullanın):

ithalat:

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;

kod:

// generates "yyyy-MM-dd" output
@JsonSerialize(using = ToStringSerializer.class)
// handles "yyyy-MM-dd" input just fine (note: "yyyy-M-d" format will not work)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate localDate;

Ve şimdi new ObjectMapper()nesnelerimi herhangi bir özel kurulum olmadan okuyabilir ve yazabilirim.


3
Eklemek istediğim bir şey, tarihi geçmek "2018-12-07"yerine, "2018-12-7"bir hata alacaksın.
Kid101

1
Doğru, (1 haneli ay veya gün) formatında yyyy-MM-dddeğil yyyy-M-d(2 haneli ay ve gün) formatında çalışır.
Shadow Man

8

Sadece Christopher cevabının bir güncellemesi.

2.6.0 sürümünden beri

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

Kullanım JavaTimeModule yerine JSR310Module (kullanımdan kaldırıldı).

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        MAPPER.registerModule(new JavaTimeModule());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return MAPPER;
    }  
}

Belgelere göre , yeni JavaTimeModule, Timezone Ids KULLANMAYAN serileştirme için aynı standart ayarları kullanır ve bunun yerine yalnızca ISO-8601 uyumlu Timezone offsetlerini kullanır.

Davranış SerializationFeature.WRITE_DATES_WITH_ZONE_ID kullanılarak değiştirilebilir.


Bu bana yardımcı oldu. Benim durumumda, MAPPER.registerModule(new JavaTimeModule());satırı eklemem gerekiyordu . LocalDate nesnelerini "2020-02-20" formatında biçimlendirmeme izin verdi. Aradığım şey için MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);hatta ihtiyacım yoktu
Cuga

6

Aşağıdaki açıklama benim için iyi çalıştı.

Ekstra bağımlılık gerekmez.

    @JsonProperty("created_at")
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime createdAt;

5
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime createdDate;

4

https://stackoverflow.com/a/53251526/1282532 , özelliği seri hale getirmenin / serisini kaldırmanın en basit yoludur. Bu yaklaşımla ilgili iki endişem var - DRY ilkesinin bir noktaya kadar ihlali ve pojo ile haritacı arasındaki yüksek eşleşme.

public class Trade {
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate tradeDate;
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate maturityDate;
    @JsonFormat(pattern = "yyyyMMdd")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    private LocalDate entryDate;
}

Birden çok LocalDate alanına sahip POJO'nuz varsa, POJO yerine eşleyiciyi yapılandırmak daha iyidir. Https://stackoverflow.com/a/35062824/1282532 kadar basit olabilirISO-8601 değerlerini ("2019-01-31") kullanıyorsanız

Özel formatı işlemeniz gerekirse, kod şu şekilde olacaktır:

ObjectMapper mapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyyMMdd")));
mapper.registerModule(javaTimeModule);

Mantık yalnızca bir kez yazılır, birden çok POJO için yeniden kullanılabilir


3

Şimdiye kadarki en basit ve en kısa:

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;

Spring boot> = 2.2+ ile bağımlılık gerekmez


1

2020 ve Jackson 2.10.1'den itibaren herhangi bir özel koda gerek yoktur, sadece Jackson'a ne istediğinizi söylemek yeterlidir:

ObjectMapper objectMapper = new ObjectMapper();

// Register module that knows how to serialize java.time objects
// Provided by jackson-datatype-jsr310
objectMapper.registerModule(new JavaTimeModule());

// Ask Jackson to serialize dates as String (ISO-8601 by default)
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

Bu yanıtta bundan daha önce bahsedilmişti , işlevselliği doğrulayan bir birim testi ekliyorum:

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.Data;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class LocalDateSerializationTest {

    @Data
    static class TestBean {
        // Accept default ISO-8601 format
        LocalDate birthDate;
        // Use custom format
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
        LocalDate birthDateWithCustomFormat;
    }

    @Test
    void serializeDeserializeTest() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();

        // Register module that knows how to serialize java.time objects
        objectMapper.registerModule(new JavaTimeModule());

        // Ask Jackson to serialize dates as String (ISO-8601 by default)
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // The JSON string after serialization
        String json = "{\"birthDate\":\"2000-01-02\",\"birthDateWithCustomFormat\":\"03/02/2001\"}";

        // The object after deserialization
        TestBean object = new TestBean();
        object.setBirthDate(LocalDate.of(2000, 1, 2));
        object.setBirthDateWithCustomFormat(LocalDate.of(2001, 2, 3));

        // Assert serialization
        assertEquals(json, objectMapper.writeValueAsString(object));

        // Assert deserialization
        assertEquals(object, objectMapper.readValue(json, TestBean.class));
    }
}

TestBean, çekirdek için kazan plakasını oluşturmak için Lombok'u kullanır.


0

Yapılandırma sınıfında LocalDateSerializer ve LocalDateDeserializer sınıflarını tanımlayın ve bunları aşağıdaki gibi JavaTimeModule aracılığıyla ObjectMapper'a kaydedin :

@Configuration
public class AppConfig
{
@Bean
    public ObjectMapper objectMapper()
    {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_EMPTY);
        //other mapper configs
        // Customize de-serialization


        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        mapper.registerModule(javaTimeModule);

        return mapper;
    }

    public class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(Constant.DATE_TIME_FORMATTER));
        }
    }

    public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {

        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return LocalDate.parse(p.getValueAsString(), Constant.DATE_TIME_FORMATTER);
        }
    }
}

0

Talebiniz buna benzer bir nesne içeriyorsa:

{
    "year": 1900,
    "month": 1,
    "day": 20
}

O zaman şunları kullanabilirsiniz:

data class DateObject(
    val day: Int,
    val month: Int,
    val year: Int
)
class LocalDateConverter : StdConverter<DateObject, LocalDate>() {
    override fun convert(value: DateObject): LocalDate {
        return value.run { LocalDate.of(year, month, day) }
    }
}

Alanın üstünde:

@JsonDeserialize(converter = LocalDateConverter::class)
val dateOfBirth: LocalDate

Kod Kotlin'de ama bu tabii ki Java için de geçerli.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.