针对Spring Boot和Feign中使用Java 8时间日期API(LocalDate等)的序列化问题,可以参考以下攻略:
问题描述
在Spring Boot的应用中,我们常常会使用Java 8的新特性,如LocalDate等时间日期API。在使用Feign进行服务间调用时,可能会遇到序列化问题,导致时间日期类不能正确转换。具体表现为服务A传递LocalDate到服务B时,会得到类似“cannot deserialize”的错误信息。
解决方法
不使用Java 8时间日期类型
一种简单方式是,不使用Java 8的时间日期类型,而使用旧版的Date和Calendar等类。但这种方式不仅繁琐,而且Java 8的时间日期API有很多新特性,值得我们使用。
使用自定义JSON序列化器和反序列化器
另一种更好的方式是,使用自定义JSON序列化器和反序列化器。这种方式需要我们在代码中添加相应的序列化和反序列化器,用于将Java 8的时间日期类型转换为JSON格式,并在服务间传递时反序列化。
下面,我们将介绍两种使用自定义JSON序列化器和反序列化器的方式,以解决Spring Boot和Feign中使用Java 8时间日期API的序列化问题。
方式一:使用Jackson
添加依赖
首先,在pom.xml文件中添加Jackson的相关依赖:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
创建自定义序列化器和反序列化器
然后,我们需要创建自定义的序列化器和反序列化器,用于将Java 8的时间日期类型转换为JSON格式,并在服务间传递时反序列化。
具体实现如下:
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
public class LocalDateSerializer extends JsonSerializer<LocalDate> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeString(formatter.format(value));
}
@Override
public void serializeWithType(LocalDate value, JsonGenerator gen, SerializerProvider serializers,
TypeSerializer typeSer) throws IOException {
serialize(value, gen, serializers);
}
}
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String dateAsString = p.getText().trim();
return LocalDate.parse(dateAsString, formatter);
}
}
以上代码中,LocalDateSerializer类用于将LocalDate转换为字符串格式,并使用DateTimeFormatter格式化为"yyyy-MM-dd"格式的字符串。LocalDateDeserializer类用于将字符串格式的日期转换为LocalDate类型。
注册自定义序列化器和反序列化器
最后,我们需要将自定义序列化器和反序列化器注册到Jackson的Object Mapper中,使其生效。在Spring Boot项目中,我们可以通过配置类实现:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
}
现在,我们就可以愉快地使用Java 8的时间日期类型了!
方式二:使用Gson
如果你使用的是Gson而不是Jackson,那么可以利用Gson的registerTypeAdapter()方法注册自定义的序列化器和反序列化器。具体步骤如下:
添加依赖
首先,在pom.xml文件中添加Gson的相关依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
创建自定义序列化器和反序列化器
然后,我们需要创建自定义的序列化器和反序列化器,用于将Java 8的时间日期类型转换为JSON格式,并在服务间传递时反序列化。
具体实现如下:
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class LocalDateSerializer implements JsonSerializer<LocalDate> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(formatter.format(src));
}
}
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public class LocalDateDeserializer implements JsonDeserializer<LocalDate> {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String dateAsString = json.getAsString().trim();
return LocalDate.parse(dateAsString, formatter);
}
}
以上代码中,LocalDateSerializer类用于将LocalDate转换为字符串格式,并使用DateTimeFormatter格式化为"yyyy-MM-dd"格式的字符串。LocalDateDeserializer类用于将字符串格式的日期转换为LocalDate类型。
注册自定义序列化器和反序列化器
最后,我们需要将自定义序列化器和反序列化器注册到Gson的GsonBuilder中,使其生效。在Spring Boot项目中,我们可以通过配置类实现:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@Configuration
public class GsonConfig {
@Bean
public Gson gson() {
return new GsonBuilder()
.registerTypeAdapter(LocalDate.class, new LocalDateSerializer())
.registerTypeAdapter(LocalDate.class, new LocalDateDeserializer())
.create();
}
}
现在,我们就可以愉快地使用Java 8的时间日期类型了!
示例
这里给出两个示例,演示如何使用自定义JSON序列化器和反序列化器:
示例一
服务A
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/date")
public LocalDate getDate() {
return LocalDate.now();
}
}
服务B
@FeignClient(name = "my-service")
public interface MyClient {
@GetMapping("/api/date")
LocalDate getDate();
}
调用方式
@RestController
public class MyController {
@Autowired
private MyClient myClient;
@GetMapping("/date")
public LocalDate getDate() {
return myClient.getDate();
}
}
以上示例中,MyController是服务B的控制器,MyClient是服务A的客户端。在调用MyClient的getDate()方法时,JsonSerializer将LocalDate对象序列化为JSON格式字符串,并返回给服务B。然后,JsonDeserializer将JSON格式字符串反序列化成LocalDate对象。
示例二
服务A
@RestController
@RequestMapping("/api")
public class MyController {
@PostMapping("/date")
public void setDate(@RequestBody LocalDate date) {
System.out.println(date);
}
}
服务B
@FeignClient(name = "my-service")
public interface MyClient {
@PostMapping("/api/date")
void setDate(LocalDate date);
}
调用方式
@RestController
public class MyController {
@Autowired
private MyClient myClient;
@GetMapping("/date")
public void setDate() {
myClient.setDate(LocalDate.now());
}
}
以上示例中,MyController是服务B的控制器,MyClient是服务A的客户端。在调用MyClient的setDate(LocalDate date)方法时,JsonSerializer将LocalDate对象序列化为JSON格式字符串,并将其作为请求体发送给服务A。然后,JsonDeserializer将请求体中的JSON格式字符串反序列化成LocalDate对象。注意,这种方式需要将请求体的Content-Type设置为application/json。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解决Spring Boot和Feign中使用Java 8时间日期API(LocalDate等)的序列化问题 - Python技术站