使用Jackson JSON映射器序列化/反序列化java 8 java.time

问题描述 投票:187回答:14

如何将Jackson JSON映射器与Java 8 LocalDateTime一起使用?

org.codehaus.jackson.map.JsonMappingException:无法从JSON String实例化类型[simple type,class java.time.LocalDateTime]的值;没有单字符串构造函数/工厂方法(通过引用链:MyDTO [“field1”] - > SubDTO [“date”])

jackson java-time
14个回答
240
投票

这里不需要使用自定义序列化器/反序列化器。使用jackson-modules-java8's datetime module

数据类型模块使Jackson能够识别Java 8 Date&Time API数据类型(JSR-310)。

This module adds support for quite a few classes:

  • 持续时间
  • 瞬间
  • localdateti到
  • LOCALDATE
  • 本地时间
  • 月日
  • OffsetDateTime
  • OffsetTime
  • YearMonth
  • ZonedDateTime
  • 了zoneid
  • ZoneOffset

2
投票

这只是一个例子,如何在我讨厌调试此问题的单元测试中使用它。关键因素是

  • mapper.registerModule(new JavaTimeModule());
  • <artifactId>jackson-datatype-jsr310</artifactId>的maven依赖

码:

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

}

1
投票

您可以在application.yml文件中设置它来解析即时时间,即java8中的Date API:

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

1
投票

对于那些使用Spring Boot 2.x的人

无需执行上述任何操作 - Java 8 LocalDateTime开箱即用地序列化/反序列化。我必须在1.x中完成上述所有操作,但是使用Boot 2.x,它可以无缝地工作。

参见此参考资料JSON Java 8 LocalDateTime format in Spring Boot


1
投票

如果您考虑使用fastjson,您可以解决您的问题,请注意版本

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

1
投票

如果在这里使用SpringBoot时有任何问题是我如何修复问题而不添加新的依赖项。

Spring 2.1.3杰克逊期望这个2019-05-21T07:37:11.000格式的日期字符串yyyy-MM-dd HH:mm:ss.SSSLocalDateTime中反序列化。确保日期字符串使用T而不是space来分隔日期和时间。秒(ss)和毫秒(SSS)可以省略。

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

68
投票

更新:由于历史原因留下这个答案,但我不推荐它。请参阅上面接受的答案。

告诉Jackson使用您的自定义[de]序列化类进行映射:

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

提供自定义类:

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

随机事实:如果我在类之上嵌套并且不使它们静态,则错误消息很奇怪:org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported


44
投票

如果您使用的是fastxml的ObjectMapper类,默认情况下ObjectMapper不了解LocalDateTime类,因此,您需要在gradle / maven中添加另一个依赖项:

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

现在您需要将此库提供的数据类型支持注册到objectmapper对象,这可以通过以下方式完成:

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

现在,在您的jsonString中,您可以轻松地将java.LocalDateTime字段设置如下:

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

通过这一切,您的Json文件到Java对象转换将正常工作,您可以通过以下方式读取该文件:

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

39
投票

这个maven依赖将解决您的问题:

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

我努力的一件事是,在反序列化期间ZonedDateTime时区被更改为GMT。事实证明,默认情况下,杰克逊用上下文中的一个替换它。为了保持区域1必须禁用此“功能”

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

16
投票

使用Spring启动时遇到了类似的问题。使用Spring引导1.5.1.RELEASE我所要做的就是添加依赖:

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

6
投票

如果您正在使用Jersey,那么您需要像其他人建议的那样添加Maven依赖项(jackson-datatype-jsr310)并注册您的对象映射器实例,如下所示:

@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时,您需要像这样添加此映射器:

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

6
投票

如果你出于某种原因不能使用jackson-modules-java8,你可以使用long@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);
    }
}

例:

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

产量

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

2
投票

我使用这个时间格式:"{birthDate": "2018-05-24T13:56:13Z}"从json反序列化为java.time.Instant(见截图)

enter image description here


2
投票

如果您正在使用Spring启动并且在OffsetDateTime中遇到此问题,那么需要使用@greperror(由2016年5月28日13:04回答)回答的registerModules,但请注意,存在一个区别。提到的依赖关系不需要添加,因为我猜测spring boot已经有了。我在使用Spring启动时遇到了这个问题,它在没有添加此依赖项的情况下对我有用。

© www.soinside.com 2019 - 2024. All rights reserved.