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


221

如何在Java 8 LocalDateTime中使用Jackson JSON映射器?

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


4
您确定要将LocalDateTime映射到JSon吗?据我所知,JSon没有日期格式,尽管JavaScript使用ISO-8601。问题是,LocalDateTime没有时区...因此,如果您使用JSON作为发送日期/时间信息的媒介,如果客户端将缺少时区的情况解释为默认UTC(或它自己的时间),则可能会遇到麻烦时区)。如果那是您想要做的,那当然很好。但是只要检查您是否考虑过使用ZonedDateTime即可
arcuri82 '16

Answers:


276

此处无需使用自定义序列化器/解串器。使用jackson-modules-java8的datetime模块

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

该模块增加了对许多类的支持:

  • 持续时间
  • 瞬间
  • LocalDateTime
  • 本地日期
  • 当地时间
  • 月日
  • OffsetDateTime
  • 偏移时间
  • 年月
  • ZonedDateTime
  • ZoneId
  • ZoneOffset

59
哦,是的!我虽然没有用,因为我没有意识到必须对mapper对象执行以下一项自定义操作: registerModule(new JSR310Module())findAndRegisterModules()。见 github.com/FasterXML/jackson-datatype-jsr310这里是如果你使用Spring框架如何自定义映射对象:stackoverflow.com/questions/7854030/...
亚历山大·泰勒

10
与我尝试过的最简单的方法不兼容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
如果你最近使用Spring框架就没有必要再定制自己,众所周知杰克逊的模块,如这一个,如果在类路径中检测到他们现在自动注册:spring.io/blog/2014/12/02/...
vorburger

36
JSR310Module已经有点过时了,请使用JavaTimeModule
Jacob Eckel

17
@MattBall我建议补充一点,除了使用jackson-datatype-jsr310外,您还需要向对象映射器注册JavaTimeModule :objectMapper.registerModule(new JavaTimeModule());。无论是序列化还是反序列化。
GrandAdmiral '17

76

更新:由于历史原因,留下了这个答案,但我不建议这样做。请参阅上面接受的答案。

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

@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


在撰写本文时,相对于其他答案,这里的唯一优势是不依赖于FasterXML,无论如何。
亚历山大·泰勒


4
哦,我明白了。我正在研究的项目中有很多古代pom条目。因此,不再需要org.codehaus,现在全部是com.fasterxml。谢谢!
亚历山大·泰勒

2
我也无法使用内置的格式,因为我需要通过格式化程序ISO_DATE_TIME。不知道在使用JSR310Module时是否可以这样做。
isaac.hazan

与春天我使用这个必须建立二者的豆LocalDateTimeSerializerLocalDateTimeDeserializer
BenR

53

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

4
findAndRegisterModules()是构建时失踪了我的关键部分ObjectMapper
Xaero Degreaz

2
那即时呢?
powder366 '18

1
我还添加了这条线: objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
珍妮(Janet)

这是所需的完整代码:ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
khush19年

42

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

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

我一直在努力的一件事是,在反序列化期间将ZonedDateTime时区更改为GMT。原来,杰克逊默认使用上下文中的一个替换它。要保留区域一,必须禁用此“功能”

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

5
非常感谢DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE提示。
Andrei Urvantcev,2016年

5
除了禁用之外,DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE我还必须禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS所有东西才能开始正常工作。
马特·沃森

16

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

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

7

如果使用的是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

这是一种非常干净的方法,它允许您的类的用户使用他们想要的任何方式。
Ashhar Hasan


3

这只是一个示例,说明如何在我用来调试此问题的单元测试中使用它。关键成分是

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

码:

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

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


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中,它可以无缝运行。

在Spring Boot中也请参阅此参考资料JSON Java 8 LocalDateTime格式


1

如果有人在使用SpringBoot此处时遇到问题,那么我将如何解决此问题而不添加新的依赖项。

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

@JsonProperty("last_charge_date")
public LocalDateTime lastChargeDate;

0

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

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

0

如果由于GraphQL Java工具而遇到此问题,并尝试Instant从日期字符串中封送Java ,则需要设置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


0

如果您使用的是Jackson序列化器,则可以使用以下方法来使用日期模块:

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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.