serialize / deserialize java 8 java.time với trình ánh xạ Jackson JSON


221

Làm cách nào để sử dụng trình ánh xạ Jackson JSON với Java 8 LocalDateTime?

org.codehaus.jackson.map.JsonMappingException: Không thể khởi tạo giá trị của loại [loại đơn giản, lớp java.time.LocalDateTime] từ Chuỗi JSON; không có phương thức xây dựng / nhà sản xuất chuỗi đơn (thông qua chuỗi tham chiếu: MyDTO ["field1"] -> SubDTO ["date"])


4
bạn có chắc chắn muốn ánh xạ LocalDateTime sang JSon không? Theo như tôi biết, JSon không có định dạng cho ngày, mặc dù JavaScript sử dụng ISO-8601. Vấn đề là, LocalDateTime không có múi giờ ... vì vậy, nếu bạn sử dụng JSON làm phương tiện để gửi thông tin ngày / giờ, bạn có thể gặp rắc rối nếu máy khách sẽ hiểu thiếu múi giờ là UTC mặc định (hoặc của chính nó Múi giờ). Nếu đó là những gì bạn muốn làm, tất nhiên nó là tốt. Nhưng chỉ cần kiểm tra xem bạn có cân nhắc sử dụng ZencedDateTime không
arcuri82

Câu trả lời:


276

Không cần sử dụng serial serial / deserialators tùy chỉnh ở đây. Sử dụng mô đun thời gian của jackson-mô-đun-java8 :

Mô-đun kiểu dữ liệu để làm cho Jackson nhận ra các kiểu dữ liệu API Ngày & Giờ của Java 8 (JSR-310).

Mô-đun này thêm hỗ trợ cho khá nhiều lớp:

  • Thời lượng
  • Tức thì
  • Thời gian cục bộ
  • Ngày địa phương
  • Giờ địa phương
  • Tháng ngày
  • OffsetDateTime
  • Thời gian bù giờ
  • Giai đoạn = Stage
  • Năm
  • Năm tháng
  • ZiatedDateTime
  • Khu vực
  • ZonePackset

59
Ồ, vâng! Tôi mặc dù điều này không hiệu quả vì tôi không nhận ra rằng người ta phải thực hiện một trong những tùy chỉnh này cho đối tượng ánh xạ: registerModule(new JSR310Module())hoặc findAndRegisterModules(). Xem github.com/FasterXML/jackson-datatype-jsr 310 và đây là cách tùy chỉnh đối tượng trình ánh xạ của bạn nếu bạn sử dụng Spring framework: stackoverflow.com/questions/7854030/ Kẻ
Alexander Taylor

10
Không hoạt động với cái đơn giản nhất tôi đã thử,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); }
Abhijit Sarkar

9
Nếu bạn sử dụng khung công tác Spring gần đây thì không cần phải tự tùy chỉnh nữa, vì các mô-đun Jackson nổi tiếng như mô-đun này hiện được đăng ký tự động nếu chúng được phát hiện trên đường dẫn lớp: spring.io/blog/2014/12/02/
vorburger

36
JSR 310Module đã hơi lỗi thời, thay vào đó hãy sử dụng JavaTimeModule
Jacob Eckel

17
@MattBall Tôi khuyên bạn nên thêm rằng, ngoài việc sử dụng jackson-datatype-jsr 310, bạn cần phải đăng ký JavaTimeModule với trình ánh xạ đối tượng của bạn : objectMapper.registerModule(new JavaTimeModule());. Cả về tuần tự hóa và khử lưu huỳnh.
GrandAdmirus

76

Cập nhật: Để lại câu trả lời này vì lý do lịch sử, nhưng tôi không khuyên bạn nên trả lời. Xin vui lòng xem câu trả lời được chấp nhận ở trên.

Yêu cầu Jackson lập bản đồ bằng các lớp tuần tự [de] tùy chỉnh của bạn:

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

cung cấp các lớp tùy chỉnh:

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());
    }
}

thực tế ngẫu nhiên: nếu tôi lồng trên các lớp và không làm cho chúng tĩnh, thông báo lỗi là lạ: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported


đối với bài viết này, lợi thế duy nhất ở đây so với câu trả lời khác không phụ thuộc vào FasterXML, bất kể đó là gì.
Alexander Taylor


4
oh tôi hiểu rồi rất nhiều mục pom cổ trong dự án tôi đang làm. vì vậy không còn org.codehaus nữa, bây giờ tất cả là com.fasterxml. cảm ơn!
Alexander Taylor

2
Ngoài ra, tôi không thể sử dụng tích hợp sẵn vì tôi cần phải vượt qua bộ định dạng ISO_DATE_TIME. Không chắc chắn nếu nó có thể làm điều đó khi sử dụng JSR 310Module.
isaac.hazan 15/03/2015

Sử dụng điều này với Spring tôi đã phải tạo ra một bean của cả hai LocalDateTimeSerializerLocalDateTimeDeserializer
BenR

53

Nếu bạn đang sử dụng lớp ObjectMapper của quickxml, theo mặc định ObjectMapper không hiểu lớp LocalDateTime, vì vậy, bạn cần thêm một phụ thuộc khác trong lớp / maven của mình:

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

Bây giờ bạn cần đăng ký hỗ trợ kiểu dữ liệu được cung cấp bởi thư viện này vào đối tượng objectmapper của bạn, điều này có thể được thực hiện bằng cách sau:

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

Bây giờ, trong jsonString của bạn, bạn có thể dễ dàng đặt trường java.LocalDateTime của mình như sau:

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

Bằng cách thực hiện tất cả điều này, tệp Json của bạn chuyển đổi đối tượng Java sẽ hoạt động tốt, bạn có thể đọc tệp bằng cách sau:

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

4
findAndRegisterModules()là một phần quan trọng đã bị thiếu đối với tôi khi xây dựng mộtObjectMapper
Xaero Degreaz

2
Còn tức thì thì sao?
bột366

1
Tôi cũng đã thêm dòng này: objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Janet

đây là mã hoàn chỉnh cần thiết: ObjectMapper objectMapper = new ObjectMapper (); objectMapper.findAndRegisterModules (); objectMapper.configure (serializationFeature.WRITE_DATE_AS_TIMESTAMPS, sai);
khush

42

Sự phụ thuộc maven này sẽ giải quyết vấn đề của bạn:

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

Một điều tôi đã đấu tranh là vì múi giờ ZencedDateTime được đổi thành GMT trong quá trình khử lưu huỳnh. Hóa ra, theo mặc định jackson thay thế nó bằng một từ ngữ cảnh .. Để giữ vùng người ta phải vô hiệu hóa 'tính năng' này

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

5
Rất cám ơn về gợi ý DeserializationFeature.ADJUST_DATE_TO_CONTEXT_TIME_ZONE.
Andrei Ureccevev

5
Cũng như vô hiệu hóa DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONEtôi cũng phải vô hiệu hóaSerializationFeature.WRITE_DATES_AS_TIMESTAMPS mọi thứ để bắt đầu hoạt động như bình thường.
Matt Watson

16

Tôi đã có một vấn đề tương tự trong khi sử dụng Spring boot . Với Spring boot 1.5.1. HÃY ĐỂ tất cả những gì tôi phải làm là thêm phụ thuộc:

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

7

Nếu bạn đang sử dụng Jersey thì bạn cần thêm phụ thuộc Maven (jackson-datatype-jsr 310) như những người khác đã đề xuất và đăng ký đối tượng trình ánh xạ đối tượng của bạn như vậy:

@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;
  }
}

Khi đăng ký Jackson vào tài nguyên của bạn, bạn cần thêm trình ánh xạ này như sau:

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

6

Nếu bạn không thể sử dụng jackson-modules-java8vì bất kỳ lý do gì, bạn có thể (hủy) tuần tự hóa trường tức thời là longsử dụng @JsonIgnore@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);
    }
}

Thí dụ:

@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());
}

sản lượng

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

Đây là một phương thức rất sạch sẽ và cho phép người dùng trong lớp của bạn sử dụng bất cứ cách nào họ muốn.
Ashhar Hasan

3

Tôi sử dụng định dạng thời gian này: "{birthDate": "2018-05-24T13:56:13Z}"để khử lưu từ json thành java.time.Instant (xem ảnh chụp màn hình)

nhập mô tả hình ảnh ở đây


3

Đây chỉ là một ví dụ về cách sử dụng nó trong một bài kiểm tra đơn vị mà tôi đã hack để gỡ lỗi vấn đề này. Các thành phần chính là

  • mapper.registerModule(new JavaTimeModule());
  • phụ thuộc maven của <artifactId>jackson-datatype-jsr310</artifactId>

Mã số:

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

Nếu bạn đang sử dụng Spring boot và gặp vấn đề này với OffsetDateTime thì cần sử dụng registerModules như đã trả lời ở trên bởi @greperror (đã trả lời 28 tháng 5, 16 lúc 13:04) nhưng lưu ý rằng có một sự khác biệt. Sự phụ thuộc được đề cập không cần phải thêm vào vì tôi đoán rằng boot mùa xuân đã có nó. Tôi đã gặp vấn đề này với Spring boot và nó đã hoạt động với tôi mà không cần thêm sự phụ thuộc này.


1

Bạn có thể đặt điều này trong application.ymltệp của mình để giải quyết Thời gian tức thì, đó là API ngày trong java8:

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

1

Dành cho những ai sử dụng Spring Boot 2.x

Không cần thực hiện bất kỳ điều nào ở trên - Java 8 LocalDateTime được tuần tự hóa / khử nối tiếp ra khỏi hộp. Tôi đã phải làm tất cả những điều trên trong 1.x, nhưng với Boot 2.x, nó hoạt động trơn tru.

Xem tham chiếu này quá định dạng JSON Java 8 LocalDateTime trong Spring Boot


1

Nếu bất kỳ ai gặp vấn đề trong khi sử dụng SpringBootở đây là cách tôi khắc phục sự cố mà không cần thêm phụ thuộc mới.

Spring 2.1.3Jackson hy vọng chuỗi ngày 2019-05-21T07:37:11.000yyyy-MM-dd HH:mm:ss.SSSđịnh dạng này sẽ hủy tuần tự hóa LocalDateTime. Đảm bảo chuỗi ngày phân tách ngày và giờ Tkhông có space. giây ( ss) và mili giây ( SSS) có thể bị hủy bỏ.

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

0

Nếu bạn cân nhắc sử dụng fastjson, bạn có thể giải quyết vấn đề của mình, lưu ý phiên bản

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

0

Nếu bạn gặp phải vấn đề này do Công cụ Java của GraphQL và cố gắng sắp xếp Java Instanttừ chuỗi ngày, bạn cần thiết lập SchemaParser của mình để sử dụng ObjectMapper với một số cấu hình nhất định:

Trong lớp GraphQLSchemaBuilder của bạn, hãy tiêm ObjectMapper và thêm các mô-đun này:

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

và thêm nó vào các tùy chọn:

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

Xem https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/32


0

Nếu bạn đang sử dụng Jackson serializer , đây là một cách để sử dụng các mô-đun ngày:

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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.