java 8 java.time'ı Jackson JSON mapper ile seri hale getirme / serisini kaldırma


221

Java 8 LocalDateTime ile Jackson JSON eşleştiricisini nasıl kullanabilirim?

org.codehaus.jackson.map.JsonMappingException: JSON String'ten [simple type, class java.time.LocalDateTime] türünün değeri somutlaştırılamıyor; tek Dizeli yapıcı / fabrika yöntemi yok (referans zinciri aracılığıyla: MyDTO ["field1"] -> SubDTO ["tarih"])


4
LocalDateTime'ı JSon ile eşlemek istediğinizden emin misiniz? Bildiğim kadarıyla, JavaScript ISO-8601 kullanıyor olsa da, JSon tarihler için bir biçime sahip değil. Sorun şu ki, LocalDateTime'ın saat dilimi yok ... Bu nedenle, tarih / saat bilgisi göndermek için JSON'u ortam olarak kullanırsanız, istemcinin saat dilimi eksikliğini varsayılan UTC (veya kendi) olarak yorumlaması durumunda sorun yaşayabilirsiniz. saat dilimi). Yapmak istediğiniz şey buysa, elbette iyidir. Ancak bunun yerine
ZonedDateTime

Yanıtlar:


276

Burada özel serileştiricilerin / serileştiricilerin kullanılmasına gerek yoktur. Kullanım jackson-modüller-java8 tarih saat modülü var :

Jackson'ın Java 8 Tarih ve Saat API'si veri türlerini (JSR-310) tanımasını sağlayan veri türü modülü.

Bu modül birkaç sınıf için destek ekler:

  • süre
  • anlık
  • LocalDateTime
  • LOCALDATE
  • Yerel zaman
  • MONTHDAY
  • OffsetDateTime
  • OffsetTime
  • dönem
  • Yıl
  • Yıl ay
  • ZonedDateTime
  • BölgeKimliği
  • ZoneOffset

59
Oh evet! Bu çalışma değildi ama ben bir eşleştirici nesnesi için bu özelleştirme birini yapmak gerektiğini fark etmedi: registerModule(new JSR310Module())veya findAndRegisterModules(). Bkz. Github.com/FasterXML/jackson-datatype-jsr310 ve Bahar çerçevesini kullanırsanız haritacı nesnenizi nasıl özelleştirebileceğiniz aşağıda açıklanmıştır: stackoverflow.com/questions/7854030/…
Alexander Taylor

10
Denediğim en OffsetDateTime @Test public void testJacksonOffsetDateTimeDeserializer() throws IOException { ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); String json = "\"2015-10-20T11:00:00-8:30\""; mapper.readValue(json, OffsetDateTime.class); }
basitle

9
Son Bahar çerçevesini kullanırsanız, artık kendinizi özelleştirmenize gerek yoktur ve bunun gibi tanınmış Jackson modülleri, sınıfyolunda algılanırsa otomatik olarak kaydedilir: spring.io/blog/2014/12/02/…
vorburger

37
JSR310Module zaten biraz modası geçmiş, bunun yerine JavaTimeModule kullanın
Jacob Eckel

17
@MattBall Jackson-datatype-jsr310 kullanmaya ek olarak, JavaTimeModule'u nesne eşleyicinize kaydetmeniz gerektiğini de eklemenizi öneririm objectMapper.registerModule(new JavaTimeModule());. Hem serileştirme hem de serileştirme üzerine.
GrandAdmiral

76

Güncelleme: Bu cevabı tarihsel nedenlerden dolayı bırakıyorum, ancak önermiyorum. Lütfen yukarıdaki kabul edilen cevaba bakınız.

Jackson'a özel [seri] serileştirme sınıflarınızı kullanarak eşlemesini söyleyin:

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime ignoreUntil;

özel sınıflar sağlayın:

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
        arg1.writeString(arg0.toString());
    }
}

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {
        return LocalDateTime.parse(arg0.getText());
    }
}

rastgele gerçek: Eğer sınıfların üzerinde yuva ve statik yapmazsanız, hata iletisi garip: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported


Bu yazıdan itibaren, burada diğer cevaba göre tek avantaj FasterXML'ye bağlı değil, ne olursa olsun.
Alexander Taylor


4
Ah anlıyorum. üzerinde çalıştığım birçok antik pom girdisi. öyleyse artık org.codehaus, hepsi com.fasterxml. Teşekkürler!
Alexander Taylor

2
Ayrıca ben biçimlendirici ISO_DATE_TIME geçmesi gerektiğinden yerleşik bir kullanamadı. JSR310Module'u kullanırken bunu yapmanın mümkün olup olmadığından emin değilim.
isaac.hazan

Bunu Spring ile birlikte kullanarak her ikisinin de bir fasulyesini oluşturmak zorunda kaldım LocalDateTimeSerializerveLocalDateTimeDeserializer
BenR

53

Fasterxml ObjectMapper sınıfını kullanıyorsanız, varsayılan olarak ObjectMapper LocalDateTime sınıfını anlamamaktadır, bu nedenle gradle / maven öğenize başka bir bağımlılık eklemeniz gerekir:

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3'

Şimdi bu kütüphane tarafından sunulan veri türü desteğini objectmapper nesnesine kaydetmeniz gerekiyor, bu aşağıdaki şekilde yapılabilir:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();

Şimdi, jsonString'inizde java.LocalDateTime alanınızı aşağıdaki gibi kolayca koyabilirsiniz:

{
    "user_id": 1,
    "score": 9,
    "date_time": "2016-05-28T17:39:44.937"
}

Tüm bunları yaparak, Json dosyanızdan Java nesnesine dönüştürme iyi çalışır, dosyayı aşağıdaki gibi okuyabilirsiniz:

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
            });

4
findAndRegisterModules()ObjectMapper
Xaero Degreaz

2
Anında Arama ne olacak?
powder366

1
Bu satırı da ekledim: objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Janet

bu gereken kodun tamamıdır: ObjectMapper objectMapper = new ObjectMapper (); objectMapper.findAndRegisterModules (); objectMapper.configure (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, yanlış);
şşşt

42

Bu maven bağımlılığı sorununuzu çözecektir:

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

Mücadele ettiğim bir şey, serileştirme sırasında ZonedDateTime saat diliminin GMT olarak değiştirilmesidir. Görünüşe göre, jackson onu varsayılan olarak bağlamdan biriyle değiştirdi. Bölgeyi korumak için bu 'özelliği' devre dışı bırakmalı

Jackson2ObjectMapperBuilder.json()
    .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)

5
DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE ipucu için çok teşekkürler.
Andrei Urvantcev

5
Devre dışı bırakmanın yanı sıra , her şeyin olması gerektiği gibi çalışmaya başlaması DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONEiçin devre dışı bırakmak zorunda kaldım SerializationFeature.WRITE_DATES_AS_TIMESTAMPS.
Matt Watson

16

Spring boot kullanırken benzer bir sorun yaşadım . Spring boot 1.5.1.RELEASE ile tek yapmam gereken bağımlılık eklemek:

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

7

Jersey kullanıyorsanız, diğerlerinin önerdiği şekilde Maven bağımlılığını (jackson-datatype-jsr310) eklemeniz ve nesne eşleyici örneğinizi şöyle kaydettirmeniz gerekir:

@Provider
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {

  final ObjectMapper defaultObjectMapper;

  public JacksonObjectMapper() {
    defaultObjectMapper = createDefaultMapper();
  }

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

  private static ObjectMapper createDefaultMapper() {
    final ObjectMapper mapper = new ObjectMapper();    
    mapper.registerModule(new JavaTimeModule());
    return mapper;
  }
}

Jackson'ı kaynaklarınıza kaydederken, bu eşleştiriciyi şu şekilde eklemeniz gerekir:

final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
  .register(JacksonObjectMapper.class)
  .register(JacksonJaxbJsonProvider.class);

6

Eğer kullanamıyorsanız jackson-modules-java8sebepten dolayı sen (de) olarak anlık alanını serileştirebilirsiniz longkullanarak @JsonIgnoreve @JsonGetter& @JsonSetter:

public class MyBean {

    private Instant time = Instant.now();

    @JsonIgnore
    public Instant getTime() {
        return this.time;
    }

    public void setTime(Instant time) {
        this.time = time;
    }

    @JsonGetter
    private long getEpochTime() {
        return this.time.toEpochMilli();
    }

    @JsonSetter
    private void setEpochTime(long time) {
        this.time = Instant.ofEpochMilli(time);
    }
}

Misal:

@Test
public void testJsonTime() throws Exception {
    String json = new ObjectMapper().writeValueAsString(new MyBean());
    System.out.println(json);
    MyBean myBean = new ObjectMapper().readValue(json, MyBean.class);
    System.out.println(myBean.getTime());
}

verim

{"epochTime":1506432517242}
2017-09-26T13:28:37.242Z

Bu çok temiz bir yöntemdir ve sınıfınızdaki kullanıcıların istedikleri şekilde kullanmalarını sağlar.
Ashhar Hasan

3

Bu zaman biçimini kullanıyorum: "{birthDate": "2018-05-24T13:56:13Z}"json'dan java.time.erial'a serisini kaldırmak için (ekran görüntüsüne bakın)

resim açıklamasını buraya girin


3

Bu sadece bu sorunu ayıklamak için kesmek bir birim testte nasıl kullanılacağı bir örnektir. Anahtar bileşenler

  • mapper.registerModule(new JavaTimeModule());
  • maven bağımlılığı <artifactId>jackson-datatype-jsr310</artifactId>

Kod:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;

class Mumu implements Serializable {
    private Instant from;
    private String text;

    Mumu(Instant from, String text) {
        this.from = from;
        this.text = text;
    }

    public Mumu() {
    }

    public Instant getFrom() {
        return from;
    }

    public String getText() {
        return text;
    }

    @Override
    public String toString() {
        return "Mumu{" +
                "from=" + from +
                ", text='" + text + '\'' +
                '}';
    }
}
public class Scratch {


    @Test
    public void JacksonInstant() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());

        Mumu before = new Mumu(Instant.now(), "before");
        String jsonInString = mapper.writeValueAsString(before);


        System.out.println("-- BEFORE --");
        System.out.println(before);
        System.out.println(jsonInString);

        Mumu after = mapper.readValue(jsonInString, Mumu.class);
        System.out.println("-- AFTER --");
        System.out.println(after);

        Assert.assertEquals(after.toString(), before.toString());
    }

}

2

Eğer bu ayarlayabilirsiniz application.ymljava8 Tarih API'sıdır gidermek Anında zaman, dosyanın:

spring.jackson.serialization.write-dates-as-timestamps=false

2

Spring önyükleme kullanıyorsanız ve OffsetDateTime ile bu sorunu yaşıyorsanız, yukarıda @greperror tarafından yanıtlandığı gibi registerModules'ü kullanmanız gerekir (13 Mayıs'ta saat 13: 28'te cevaplandı), ancak bir fark olduğunu unutmayın. Bahsedilen bağımlılığın eklenmesi gerekmez, çünkü bahar botunun zaten var olduğunu tahmin ediyorum. Spring boot ile bu sorunu yaşıyordum ve bu bağımlılığı eklemeden benim için çalıştı.


1

Spring Boot 2.x kullananlar için

Yukarıdakilerden herhangi birine gerek yoktur - Java 8 LocalDateTime kutunun dışında serileştirilir / serileştirilir. Yukarıdakilerin hepsini 1.x'te yapmak zorunda kaldım, ancak Boot 2.x ile sorunsuz çalışıyor.

Bu referansa da bakın Spring Boot'daki JSON Java 8 LocalDateTime biçimi


1

Herhangi biri kullanırken sorun yaşıyorsa SpringBootBurada yeni bağımlılık eklemeden sorunu nasıl düzelttim.

In Spring 2.1.3Jackson tarih dize bekliyor 2019-05-21T07:37:11.000bu yyyy-MM-dd HH:mm:ss.SSSde-serialize için formatında LocalDateTime. Tarih dizesinin tarih ve saati Tdeğil ile ayırdığından emin olun space. saniye ( ss) ve milisaniye ( SSS) takılabilir.

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

1

Eğer kullanıyorsanız Jackson Serializer , burada tarih modüllerini kullanmak için bir yoludur:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.apache.kafka.common.serialization.Serializer;

public class JacksonSerializer<T> implements Serializer<T> {

    private final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new ParameterNamesModule())
            .registerModule(new Jdk8Module())
            .registerModule(new JavaTimeModule());

    @Override
    public byte[] serialize(String s, T object) {
        try {
            return mapper.writeValueAsBytes(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

0

Fastjson kullanmayı düşünüyorsanız, sorununuzu çözebilir, sürümü not edebilirsiniz

 <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.56</version>
 </dependency>

0

GraphQL Java Tools nedeniyle bu sorunu yaşıyorsanız ve Instantbir tarih dizesinden Java'yı marshal etmeye çalışıyorsanız , SchemaParser'ınızı belirli yapılandırmalarla bir ObjectMapper kullanacak şekilde ayarlamanız gerekir:

GraphQLSchemaBuilder sınıfınızda ObjectMapper'i enjekte edin ve şu modülleri ekleyin:

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

ve seçeneklere ekleyin:

final SchemaParserOptions options = SchemaParserOptions.newOptions()
            .objectMapperProvider(fieldDefinition -> objectMapper)
            .typeDefinitionFactory(new YourTypeDefinitionFactory())
            .build();

Bkz. Https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/32

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.