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

时间:2015-01-14 21:08:29

标签: jackson java-time

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

  

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

20 个答案:

答案 0 :(得分:224)

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

  

数据类型模块让杰克逊认识到Java 8 Date& Time API数据类型(JSR-310)。

This module adds support for quite a few classes:

  • 持续时间
  • 即时
  • LocalDateTime
  • LOCALDATE
  • 本地时间
  • MONTHDAY
  • OffsetDateTime
  • OffsetTime
  • 期间
  • YearMonth
  • ZonedDateTime
  • 了zoneid
  • ZoneOffset

答案 1 :(得分:63)

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

告诉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

答案 2 :(得分:42)

如果你使用的是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>>() {
            });

答案 3 :(得分:39)

此maven依赖项将解决您的问题:

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

我遇到的一件事就是在反序列化过程中将ZonedDateTime时区更改为GMT。 原来,默认情况下杰克逊用上下文中的一个替换它。 要保留第1区,必须禁用此功能&#39;

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

答案 4 :(得分:16)

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

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

答案 5 :(得分: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 :(得分:6)

如果您出于某种原因无法使用jackson-modules-java8,则可以使用long@JsonIgnore&amp;&amp ;; @JsonGetter将该即时字段序列化为@JsonSetterpublic 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

产量

01/10/2017 5 1/2 Liverpool

答案 7 :(得分:3)

如果您使用的是 Jackson Serializer ,则可以使用以下日期模块:

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

答案 8 :(得分:2)

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

enter image description here

答案 9 :(得分:2)

如果由于GraphQL Java工具而遇到此问题,并尝试从日期字符串中封送Java Instant,则需要设置SchemaParser以使用具有某些配置的ObjectMapper:

在GraphQLSchemaBuilder类中,注入ObjectMapper并添加以下模块:

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

并将其添加到选项:

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

请参见https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/32

答案 10 :(得分:1)

如果您使用的是Spring Boot,并且OffsetDateTime出现此问题,则需要使用上述@greperror(在16年5月28日在13:04回答)回答的registerModules,但请注意有一个区别。不需要添加提到的依赖项,因为我猜测spring boot已经有了它。我在使用Spring Boot时遇到了这个问题,它对我有用,而没有添加此依赖项。

答案 11 :(得分:1)

如果有人在使用SpringBoot时遇到问题,这是我在不添加新依赖项的情况下解决问题的方法。

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

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

答案 12 :(得分:1)

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

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

答案 13 :(得分:1)

对于使用 Spring Boot 2.x

的用户

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

也请参阅此参考文献JSON Java 8 LocalDateTime format in Spring Boot

答案 14 :(得分:1)

您可以在application.yml文件中进行设置以解决即时时间,即Java8中的Date API:

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

答案 15 :(得分:1)

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

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

}

答案 16 :(得分:0)

所有您需要知道的是Jackson文档 https://www.baeldung.com/jackson-serialize-dates

Ad.9很快为我解决了这个问题。

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

答案 17 :(得分:0)

arrayOfFolders.each do |folders|
imap.examine(folders) # takes each folder
# Rest of the code
end

添加这些依赖项并启用这些模块。应该会帮助

        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-parameter-names</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jdk8</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

答案 18 :(得分:0)

不幸的是,这里提出的解决方案在我的环境中不起作用。 但老实说,使用 java8 时间对象作为 DTO 终究不是一个好主意。

我建议改为创建自定义 DTO,并且不要依赖不稳定的库,这可能会在下一个 jdk 版本后中断。这种方法也符合反腐败层和适配器模式的良好实践。

以下是 DTO 的示例:

public class ReportDTO implements Serializable {

    private YearMonthDTO yearMonth;

    public YearMonthDTO getYearMonth() {
        return yearMonth;
    }

    public void setYearMonth(final YearMonthDTO yearMonth) {
        this.yearMonth = yearMonth;
    }

    public void fromYearMonth(final YearMonth yearMonth) {
        this.yearMonth = new YearMonthDTO(yearMonth.getYear(), 
                              yearMonth.getMonthValue());
    }

}

public static class YearMonthDTO {

    private int year;

    private int monthValue;

    public YearMonthDTO() {

    }

    public YearMonthDTO(int year, int monthValue) {
        this.year = year;
        this.monthValue = monthValue;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonthValue() {
        return monthValue;
    }

    public void setMonthValue(int monthValue) {
        this.monthValue = monthValue;
    }

}

这当然取决于您的情况以及您必须使用此解决方案进行的工作量。与任何模式一样,此解决方案并非适用于所有情况。

无论如何,当前的最佳答案似乎不再有效。我没有尝试其他解决方案,但我决定在我的简单案例中不依赖任何库。

答案 19 :(得分:0)

我想为 Spring 的 DurationStyle parsing 提供支持,在我使用 Jackson 反序列化的自定义配置文件中的属性文件中提供支持,例如将 20s 序列化为 Duration PT20S。我通过在用于相同目的的 ObjectMapper 实例上注册自定义反序列化器来做到这一点:

@Bean("customConfigMapper")
public ObjectMapper customConfigMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    final SimpleModule module = new SimpleModule();
    module.addDeserializer(Duration.class, new SpringDurationStyleDeserializer());
    mapper.registerModule(module);
    return mapper;
}

public static class SpringDurationStyleDeserializer extends JsonDeserializer<Duration> {
    @Override
    public Duration deserialize(JsonParser jsonParser, DeserializationContext __) throws IOException {
        return Optional.ofNullable(jsonParser.getText()).map(DurationStyle::detectAndParse).orElse(null);
    }
}