场景:在controller编写的接口,在前后端交互过程中一般都会涉及到时间字段的交互,比如:后端给前端返的数据有时间相关的字段,同样,前端也存在传时间相关的字段给后端,最原始的方式就是前后端都先转换成字符串和时间戳后进行传输,收到后再进行转换,特别麻烦。
为了方便可以使用注解或者配置做到时间字段的自动转换,这里列举两种简单的操作。
方式一、使用注解
就是在日期字段上添加对应的注解(@jsonformat),例:
@jsonformat(timezone = "gmt+8", pattern = "yyyy-mm-dd hh:mm:ss")
private localdatetime createtime2;
@jsonformat(timezone = "gmt+8", pattern = "yyyy-mm-dd hh:mm:ss")
private date createtime3;优点:灵活、清晰。
缺点:所有需要转换的字段都需要手动添加、并且只支持时间和指定格式字符串互转,不支持时间戳
方式二、统一配置
一劳永逸的方式,支持时间和 时间戳、字符串 之间的互转
package com.zhh.demo.config;
import cn.hutool.core.date.localdatetimeutil;
import cn.hutool.core.util.numberutil;
import com.fasterxml.jackson.core.jsongenerator;
import com.fasterxml.jackson.core.jsonparser;
import com.fasterxml.jackson.databind.deserializationcontext;
import com.fasterxml.jackson.databind.jsondeserializer;
import com.fasterxml.jackson.databind.jsonserializer;
import com.fasterxml.jackson.databind.serializerprovider;
import com.fasterxml.jackson.databind.ser.std.tostringserializer;
import org.apache.commons.lang3.stringutils;
import org.springframework.boot.autoconfigure.jackson.jackson2objectmapperbuildercustomizer;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import java.io.ioexception;
import java.math.biginteger;
import java.time.localdatetime;
import java.time.zoneoffset;
import java.time.format.datetimeformatter;
import java.time.format.datetimeparseexception;
@configuration
// 表示通过aop框架暴露该代理对象,aopcontext能够访问
//@enableaspectjautoproxy(exposeproxy = true)
public class applicationconfig {
/** 年-月-日 时:分:秒 */
private static final string default_date_time_format = "yyyy-mm-dd hh:mm:ss";
/**
* 序列化 时间格式转换类型
* timestamp:时间戳
* datestring: 时间字符串格式
* */
private static final string local_date_time_serializer_type = "datestring";
/**
* description:适配自定义序列化和反序列化策略
*/
@bean
public jackson2objectmapperbuildercustomizer jackson2objectmapperbuildercustomizer() {
return builder -> {
// 时间的序列化和反序列化
builder.serializerbytype(localdatetime.class, new localdatetimeserializer());
builder.deserializerbytype(localdatetime.class, new localdatetimedeserializer());
// 将long类型数据转化为string给前端 避免前端造成的精度丢失
builder.serializerbytype(long.class, tostringserializer.instance);
builder.serializerbytype(biginteger.class, tostringserializer.instance);
};
}
/**
* description:序列化
* localdatetime序列化为毫秒级时间戳
*/
public static class localdatetimeserializer extends jsonserializer<localdatetime> {
@override
public void serialize(localdatetime value, jsongenerator gen, serializerprovider serializers)
throws ioexception {
if (value != null) {
// 通过配置决定把时间转换成 时间戳 或 时间字符串
if ("timestamp".equals(local_date_time_serializer_type)) {
// 13位时间戳
gen.writenumber(localdatetimeutil.toepochmilli(value));
} else {
// 指定格式的时间字符串
gen.writestring(value.format(datetimeformatter.ofpattern(default_date_time_format)));
}
}
}
}
/**
* description:反序列化
* 毫秒级时间戳序列化为localdatetime
*/
public static class localdatetimedeserializer extends jsondeserializer<localdatetime> {
@override
public localdatetime deserialize(jsonparser p, deserializationcontext deserializationcontext)
throws ioexception {
//2023年11月2日: 尝试反序列增加更多的支持, 支持long输入, 支持字符串输入
if (p == null) {
return null;
}
string source = p.gettext();
return parse(source);
}
}
/**
* 时间戳字符串或格式化时间字符串转换为 localdatetime
* 例:1745806578000、2025-04-28 10:16:18
* @param source 13位时间戳 或格式化时间字符串
* @return
*/
private static localdatetime parse(string source) {
// 如果是时间戳
if (numberutil.islong(source)) {
long timestamp = long.parselong(source);
if (timestamp > 0) {
return localdatetimeutil.of(timestamp, zoneoffset.of("+8"));
} else {
return null;
}
// 如果是格式化时间字符串
} else {
if (stringutils.isblank(source)) {
return null;
}
// 尝试判断能否解析
if (canparsebydatetimeformatter(source, datetimeformatter.ofpattern(default_date_time_format))) {
return localdatetime.parse(source, datetimeformatter.ofpattern(default_date_time_format));
}
return null;
}
}
/**
* 判断是否能解析格式化时间字符串
* @param source 格式化时间字符串(格式 2025-04-28 10:16:18)
* @param datetimeformatter 格式 yyyy-mm-dd hh:mm:ss
* @return
*/
private static boolean canparsebydatetimeformatter(string source, datetimeformatter datetimeformatter) {
try {
datetimeformatter.parse(source);
} catch (datetimeparseexception e) {
return false;
}
return true;
}
}测试一下:
在上面配置的基础上添加部分测试使用的代码
用于前后端交互的bean对象:
@data
public class userro {
private localdatetime createtime1;
private localdatetime createtime2;
@jsonformat(timezone = "gmt+8", pattern = "yyyy-mm-dd hh:mm:ss")
private date createtime3;
}controller层接口:
@apioperation("时间测试")
@postmapping("/time")
public userro show(@requestbody userro userro){
system.out.println("\n接收到的入参:");
system.out.println(userro.getcreatetime1());
system.out.println(userro.getcreatetime2());
system.out.println(userro.getcreatetime3());
system.out.println("\n出参:");
localdatetime newtime = localdatetime.now();
userro user = new userro();
user.setcreatetime1(newtime);
user.setcreatetime2(newtime);
user.setcreatetime3(new date());
system.out.println(user.getcreatetime1());
system.out.println(user.getcreatetime2());
system.out.println(user.getcreatetime3());
system.out.println("\n");
return user;
}
通过配置类,可以配置后端接口给调用方返的时间格式【字符串、时间戳】、接口调用方传的入参可以是字符串也可以的时间戳,后端会自动解析。
到此这篇关于java controller接口出入参时间序列化转换操作方法(两种)的文章就介绍到这了,更多相关java controller接口出入参内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论