Json的序列化方式有很多种,常见的有FastJson
、Gson
、Jackson
,下面将对FastJson的LocalDateTime序列化使用及源码进行分析讲解。
先上结论:
FastJson的序列化与反序列化均支持
yyyy-MM-dd HH:mm:ss
、yyyy-MM-ddTHH:mm:ss
的格式,但是不支持[yyyy, MM, dd, HH, mm, ss]
这种格式,这种数组的格式是使用Jackson
常见的,在实际使用中这种格式在跨服务对接中易产生序列化反序列化不匹配的问题,不建议使用。
推荐使用yyyy-MM-ddTHH:mm:ss
格式。
1. maven配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
2. FastJson序列化LocalDateTime样例
public class FastJsonTest {
public static void main(String[] args) {
String str = "{\"localDateTime\":\"2020-10-10 10:10:10\"}";
Admin admin = JSON.parseObject(str, Admin.class);
System.out.println("POJO toString: " + admin);
// out => POJO toString: FastJsonTest.Admin(localDateTime=2020-10-10T10:10:10)
String json = JSON.toJSONString(admin);
System.out.println("Json toString: " + json);
// out => Json toString: {"localDateTime":"2020-10-10T10:10:10"}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Admin{
private LocalDateTime localDateTime;
}
}
3. 源码分析
-
反序列化 - parseObject
核心代码为com.alibaba.fastjson.parser.deserializer.Jdk8DateCodec
的deserialze
方法。以下是核心代码。
- Jdk8DateCodec.deserialze()
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, String format, int feature) {
...
if (type == LocalDateTime.class) {
LocalDateTime localDateTime;
// TODO: 当格式为 2020-10-10 或者 20201010 或者 2020/10/10 均可以被解析
if (text.length() == 10 || text.length() == 8) {
LocalDate localDate = parseLocalDate(text, format, formatter);
localDateTime = LocalDateTime.of(localDate, LocalTime.MIN);
} else {
// TODO: 2020-10-10 10:10:10 进入此方法, 注意: formatter现在为null
localDateTime = parseDateTime(text, formatter);
}
return (T) localDateTime;
...
- Jdk8DateCodec.parseDateTime()
protected LocalDateTime parseDateTime(String text, DateTimeFormatter formatter) {
if (formatter == null) {
if (text.length() == 19) {
char c4 = text.charAt(4);
char c7 = text.charAt(7);
char c10 = text.charAt(10);
char c13 = text.charAt(13);
char c16 = text.charAt(16);
if (c13 == ':' && c16 == ':') {
if (c4 == '-' && c7 == '-') {
// TODO: 匹配 2020-10-10T10:10:10
if (c10 == 'T') {
formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// TODO: 匹配 2020-10-10 10:10:10
} else if (c10 == ' ') {
formatter = defaultFormatter;
}
。。。
// TODO: 针对各种格式匹配对应的DateTimeFormatter
。。。
return formatter == null ? //
LocalDateTime.parse(text) //
: LocalDateTime.parse(text, formatter);
}
- Jdk8DateCodec默认的
DateTimeFormatter
非常多,通过反序列化字符串格式来匹配对应的DateTimeFormatter
。
public static final Jdk8DateCodec instance = new Jdk8DateCodec();
private final static String defaultPatttern = "yyyy-MM-dd HH:mm:ss";
private final static DateTimeFormatter defaultFormatter = DateTimeFormatter.ofPattern(defaultPatttern);
private final static DateTimeFormatter defaultFormatter_23 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
private final static DateTimeFormatter formatter_dt19_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
private final static DateTimeFormatter formatter_dt19_cn = DateTimeFormatter.ofPattern("yyyy年M月d日 HH:mm:ss");
private final static DateTimeFormatter formatter_dt19_cn_1 = DateTimeFormatter.ofPattern("yyyy年M月d日 H时m分s秒");
private final static DateTimeFormatter formatter_dt19_kr = DateTimeFormatter.ofPattern("yyyy년M월d일 HH:mm:ss");
private final static DateTimeFormatter formatter_dt19_us = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss");
private final static DateTimeFormatter formatter_dt19_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
private final static DateTimeFormatter formatter_dt19_de = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
private final static DateTimeFormatter formatter_dt19_in = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
private final static DateTimeFormatter formatter_d8 = DateTimeFormatter.ofPattern("yyyyMMdd");
private final static DateTimeFormatter formatter_d10_tw = DateTimeFormatter.ofPattern("yyyy/MM/dd");
private final static DateTimeFormatter formatter_d10_cn = DateTimeFormatter.ofPattern("yyyy年M月d日");
private final static DateTimeFormatter formatter_d10_kr = DateTimeFormatter.ofPattern("yyyy년M월d일");
private final static DateTimeFormatter formatter_d10_us = DateTimeFormatter.ofPattern("MM/dd/yyyy");
private final static DateTimeFormatter formatter_d10_eur = DateTimeFormatter.ofPattern("dd/MM/yyyy");
private final static DateTimeFormatter formatter_d10_de = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private final static DateTimeFormatter formatter_d10_in = DateTimeFormatter.ofPattern("dd-MM-yyyy");
private final static DateTimeFormatter ISO_FIXED_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
private final static String formatter_iso8601_pattern = "yyyy-MM-dd'T'HH:mm:ss";
private final static String formatter_iso8601_pattern_23 = "yyyy-MM-dd'T'HH:mm:ss.SSS";
private final static String formatter_iso8601_pattern_29 = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS";
private final static DateTimeFormatter formatter_iso8601 = DateTimeFormatter.ofPattern(formatter_iso8601_pattern);
至此,LocalDateTime
的反序列化核心流程结束。
- 序列化 - toJSONString
- Jdk8DateCodec.write()
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
int features) throws IOException {
...
if (fieldType == LocalDateTime.class) {
final int mask = SerializerFeature.UseISO8601DateFormat.getMask();
LocalDateTime dateTime = (LocalDateTime) object;
String format = serializer.getDateFormatPattern();
if (format == null) {
if ((features & mask) != 0 || serializer.isEnabled(SerializerFeature.UseISO8601DateFormat)) {
format = formatter_iso8601_pattern;
} else if (serializer.isEnabled(SerializerFeature.WriteDateUseDateFormat)) {
format = JSON.DEFFAULT_DATE_FORMAT;
} else {
int nano = dateTime.getNano();
if (nano == 0) {
// TODO: 默认采用iso8601规范 yyyy-MM-dd'T'HH:mm:ss
// 格式进行序列化
format = formatter_iso8601_pattern;
} else if (nano % 1000000 == 0) {
format = formatter_iso8601_pattern_23;
} else {
format = formatter_iso8601_pattern_29;
}
}
}
if (format != null) {
// 执行序列化
write(out, dateTime, format);
} else {
...
}
额外知识点: iso8601规范
- Jdk8DateCodec.write()
private void write(SerializeWriter out, TemporalAccessor object, String format) {
DateTimeFormatter formatter;
...
if (format == formatter_iso8601_pattern) {
formatter = formatter_iso8601;
} else {
formatter = DateTimeFormatter.ofPattern(format);
}
String text = formatter.format((TemporalAccessor) object);
// out => 2020-10-10T10:10:10
out.writeString(text);
}
至此,LocalDateTime
的序列化核心流程结束。
4. 拓展: 如果想序列化为yyyy-MM-dd HH:mm:ss
格式,要如何操作
-
方法1
序列化的时候采用toJSONStringWithDateFormat
方法,设定format
。
public static void main(String[] args) {
String str = "{\"localDateTime\":\"2020-10-10 10:10:10\"}";
Admin admin = JSON.parseObject(str, Admin.class);
System.out.println("POJO toString: " + admin);
String json = JSON.toJSONStringWithDateFormat(admin, "yyyy-MM-dd HH:mm:ss");
System.out.println("Json toString: " + json);
}
-
方法2
添加@JSONField(format = "yyyy-MM-dd HH:mm:ss")
注解,在序列化和反序列化时都会严格遵守format
格式,否则抛异常。
import com.alibaba.fastjson.annotation.JSONField;
...
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Admin{
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
}